C# Base or Template Class? - c#

Ok so I am making a game using XNA, I would like all of the enemies to extend from one base class called "baseEnemy.cs". For example, I would like a zombie to have a class called "zombie.cs" but make it entend the "baseEnemy.cs".
I think I remember being told its would be laid out like:
class zombie : baseEnemy
{
}
But I am assuming the use of get{} and set{} would help me to change values of current variables in zombies.cs that exist as part of baseEnemy.cs... If that makes sense? I don't understand the usage of get{} and set{} but I have seen it in other languages (such as the code for minecraft) which I would assume are similar in their working.
So, say I have a float for the speed of the enemy... I don't want all the enemies to move at the same speed (zombie's should have a low speed, etc). How could I get the speed from the baseEnemy.cs and set it as the speed in zombie.cs.
Or would I be better just making the variables public in baseEnemy.cs?
Sorry if the above doesn't make much sense, I am not too experienced with XNA or terminology used and therefore I probably sound like I am going round in circles :S

You are looking for so called abstract methods or abstract properties.
abstract class Enemy
{
public abstract float GetSpeed();
}
class Zombie : Enemy
{
public override float GetSpeed()
{
return 10;
}
}
Note the abstract keyword preceding the class name and the method name. The child class has to implement all abstract members, if it is not abstract itself. When an abstract member is implemented the override keyword must be used.
The get set syntax you are describing is called a property. It is a special C# construct that organizes the getter and/or setter of a field and puts them in a single block. The same example as above using properties:
abstract class Enemy
{
public abstract float Speed { get; }
}
class Zombie : Enemy
{
public override float Speed
{
get { return 10; }
}
}

Related

How can I manage different weapons in manager script?

I'm making my rpg game in unity. As I need a lot of different weapons, I tried to make a script for each weapons. Then instead of enacting the Attack function in each weapon's object, I wanted to controll them in other class such as WeaponManager for some reason.
However, I got no idea how to manage variety of classes. It doesn't seem efficient to write all the codes for each classes, such as
if((gameObject).name=="Wand")
gameObject.Getcomponent<Wand>().Attack();
else if((gameObject).name=="Sword")
gameObject.Getcomponent<Sword>().Attack();
... and so on.
In other way, I also thought of SendMessage function, but it doesn't seem like efficient as well.
I'm wodering how can I solve this problem. Which method can I use?
Classical example use case for object oriented programming:
Inheritance!
Use a shared parent class both inherit from and either implement the method virtual with a shared default behavior the inheriting classes can overwrite/extend or make it abstract so inheriting classes have to implement it.
public abstract class Weapon : MonoBehaviour
{
public abstract void Attack();
// alternatively implement some default behavior
// in this case the child classes can but don't have to override this
//public virtual void Attack()
//{
// Debug.Log("Harr Harr .. but I'll do nothing else!", this);
//}
}
and then
public class Wand : Weapon
{
public override void Attack()
{
...
}
}
and
public class Sword : Weapon
{
public override void Attack()
{
...
}
}
then simply go
gameObject.GetComponent<Weapon>().Attack();

Right way to set abstract class variable value

I have camera script class that do culling task and it contains these variables and an event :
protected float CullDetailSmall = 25.0f;
protected float CullDetailMedium = 80.0f;
protected float CullDetailLarge = 130.0f;
protected float CullDetailExtraLarge = 250.0f;
protected float CullDetailXExtraLarge = 450.0f;
protected float CullDetailXXExtaLarge = 650.0f;
public virtual void Awake(){
//culling apply logic using above variable values
}
The camera script class is the base class for CamFly and CamWalk. Now i want to change the base class camera script variable values, so I make this function in each class (CamFly and CamWalk)
public void SetCullingValues(int cullDetailSmall
, int cullDetailMedium
, int cullDetailLarge
, int cullDetailExtraLarge
, int cullDetailXExtraLarge
, int cullDetailXXExtaLarge
, int CullFloor
)
{
base.CullDetailSmall = cullDetailSmall;
base.CullDetailMedium = cullDetailMedium;
base.CullDetailLarge = cullDetailLarge;
base.CullDetailExtraLarge = cullDetailExtraLarge;
base.CullDetailXExtraLarge = cullDetailXExtraLarge;
base.CullDetailXXExtaLarge = cullDetailXXExtaLarge;
base.CullFloor = CullFloor;
base.Awake();
}
It is working fine and doing what i want but its certainly not a good piece of code. I am amzed that how can i do it correctly?? Remember
i am calling above function under some conditions, like if some
condition are matched then execute above function and change base
class variable.
second i want to this for both inherited members.
Please check the next link from Microsoft with relevant abstract class documentation and best practices.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract
An abstract class is used as a base template for derived classes. It is used to enforce a design rule.
abstract class YourClass
{
public int a;
public abstract void A();
}
class Example : YourClass
{
public override void A()
{
Console.WriteLine("Example.A");
base.a++;
}
}
have Camera script class that do culling task and it contains these variables and an event
I see no event, I see a void returning virtual method named Awake.
It is working fine and doing what i want but its certainly not a good piece of code
What makes you think that? Yes, its improvable but I've seen far worse.
I am amzed that how can i do it correctly
Yeah, that happens to all of use sometimes...
My two cents of advice:
In general, do not expose fields directly. If the fields are subject to modification, use read/write properties. This way you can always ensure that the state of your base class remains consistent.
Name methods appropiately so the name conveys what the method does. SetCullingValues does not make it clear that the method will also call Awake. Either call it SetCullingValuesAndAwake or do not call Awake.
Why is SetCullingValues even declared in the derived types? Declare it in the base type.
1 and 3 assumes you have access to Camera. If you don't then there is not much you can do to improve what you already have.

Can I use an abstract base class as a Unity Editor element?

I'm trying to create a component for a Unity GameObject, let's call it MediaController. I want it to be able to manage timing (play/pause/etc) for different media (audio/video). I created an abstract class PlayableMedia with basic properties/fields/methods and created 2 classes, PlayableVideo and PlayableAudio, that inherit and implement according to our needs.
The intent was to have a singular list of PlayableMedia that could be audio/video agnostic, allowing an easy (i.e.) media.Play() call regardless of type at specific app times... but my field public List<PlayableMedia> MediaList; is not appearing in the editor and there is no error.
So ultimately my question is as the title states: is it possible to use the PlayableMedia class as the type of a field?
I'm suspecting "no" based on my experiences with this, but I've found links that say "yes" or "yes, sort of" that seem to point to custom editors/inspectors/drawers, but I have 0 experience with those and haven't been able to get it implemented (see below).
[System.Serializable]
public class RegisteredMedia
{
public float StartTime;
public PlayableMedia Media;
}
[CustomPropertyDrawer(typeof(RegisteredMedia))]
class RegisteredMediaDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), new GUIContent("Playable Media"));
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
Rect rectStartTime = new Rect(position.x, position.y, 30, position.height);
Rect rectMedia = new Rect(position.x + 35, position.y, 50, position.height);
EditorGUI.PropertyField(rectStartTime, property.FindPropertyRelative("StartTime"), GUIContent.none);
EditorGUI.PropertyField(rectMedia, property.FindPropertyRelative("Media"), GUIContent.none);
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
public class MediaController : MonoBehaviour
{
public List<RegisteredMedia> MediaList = new List<RegisteredMedia>();
\\[...] rest of implementation
}
Can anyone help me out? Either confirm that it isn't possible, or help me with an implementation if it is?
Also, if it can be done with custom editors/inspectors/drawers, can someone help me get a single item in the List<RegisteredMedia> to display as Start Time ____ Playable Media [=====] (where PlayableMedia will be a GameObject with the proper component attached)?
Be careful of your use of the word "property". In C# it means something very specific.
is it possible to use the PlayableMedia class as the type of a property?
I think you are asking the wrong question here. Rather than coming up with a new implementation, consider why your current implementation might not be working?
Firstly, I'll give you the following example:
public abstract class Car : MonoBehaviour { }
public class Jeep : Car { }
public class Ferrari : Car { }
public class CarHolder : MonoBehaviour
{
public List<Car> Cars;
}
In this example, I could create a GameObject with the CarHolder component, and was able to attach both Jeep and Ferrari Objects. It is important to note that each monoBehavior class I defined sits in its own file and the file name matches the class name. This is just how Unity works.
So to answer the question I think you are asking (assuming we replace "property" with "field"), it is indeed possible to use abstract class types and have them show up in the inspector. I suspect that you need to separate your classes into separate files.
It's possible natively since 2019.3 release via [SerializeReference] attribute https://docs.unity3d.com/ScriptReference/SerializeReference.html
e.g.
using System.Collections.Generic;
using UnityEngine;
using System;
[Serializable]
public abstract class AbstractExample {
public int foo;
}
// [Serializable] not needed here
public class ConcreteExample : AbstractExample {
}
public class Consumer : MonoBehaviour {
[SerializeReference]
public List<AbstractExample> examples = new() { new ConcreteExample() };
// both the list and the concrete instance visible in the editor
// and editable without any additional editor extensions
// note that you can't effectively add new items to the list via editor
// since that way you create a faulty abstract-ish instances instead
// (no actual way for the editor to know what subtype do you want)
// if you're OK with having the base class being not explicitly abstract
// and can provide a sensible constructor for it, just drop the abstract
// you'll still have full polymorphism support etc. with SerializeReference
}

'Does not implement Interface Member' error

I'm fairly new to C# so I'm still getting used to it.
I've had this code going where I want to control a character with a virtual joystick. I was testing whether the joystick would be responsive or now, but every time I try to play the scene, I get 3 CS0535 errors saying:
'VirtualJoystick' does not implement interface member UnityEngine.EventSystems.IPointerUpHandler.OnPointerUp(UnityEngine.EventSystems.PointerEventData)'
'VirtualJoystick' does not implement interface member UnityEngine.EventSystems.IDragHandler.OnDrag(UnityEngine.EventSystems.PointerEventData)'
'VirtualJoystick' does not implement interface member `UnityEngine.EventSystems.IPointerDownHandler.OnPointerDown(UnityEngine.EventSystems.PointerEventData)'
Here's the code. It's fairly short.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
public class VirtualJoystick : MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler
{
private Image bgImg;
private Image joystickImg;
private Vector3 inputVector;
private void start()
{
bgImg = GetComponent<Image>();
joystickImg = transform.GetChild(0).GetComponent<Image>();
}
public virtual void onDrag(PointerEventData ped)
{
Vector2 pos;
if(RectTransformUtility.ScreenPointToLocalPointInRectangle(bgImg.rectTransform, ped.position, ped.pressEventCamera, out pos))
{
Debug.Log("Test");
}
}
public virtual void onPointerDown(PointerEventData ped)
{
onDrag(ped);
}
public virtual void onPointerUp(PointerEventData ped)
{
}
}
I hope you guys could help me through this.
EDIT:
I have corrected the syntax, which was cause of why the scene was not playing. But the Debug.Log on OnDrag code is not working. I'm getting this NullReferenceException: Object reference not set to an instance of an object error. Again, I'm just following what the video says, and his seems to work just fine.
You have declared the methods virtual, since the base class declares them virtual already, you have to declare them with override.
So:
public override void OnPointerUp(PointerEventData ped) { }
Also note that with C# your naming should match exact, so OnPointerUp instead of onPointerUp.
Correct your spelling, i.e. onPointerDown -> OnPointerDown. C# is case sensitive.
When your class inherit from interface(s), it should implement all methods and properties declared on such interface(s).
In your case you must declare in your class the methods mentioned in the compiling error messages, respecting their parameters type and returning values, all case sensitive.
Interfaces are meant to make sure all classes that inherit from them implement such methods and properties. Therefore you could access those members without more knowledge about the class itself.
Interfaces do not declare method/property modifiers (private, public, etc). You can specify them as you see fit.
idraghandler,ipointeruphandler,ipointerdownhandler implement in this order

How to dependency inject a class / type?

I'm struggling with a design problem and I don't want my code to become a mess because of a bad solution. Rather than give a poor analogy I'll just explain my exact case.
I'm trying to write a clone of Wii Play Tanks, and I'm having trouble designing the Tank classes. Tank itself is the only such class, it uses dependency injection for its parts. The two parts right now are TankAI and TankWeapon. The AI handles decisions about movement and firing, the weapon describes how the weapon behaves - what projectiles it fires, and how often, etc. I have a factory class that builds tanks in different combinations.
My projectile classes are set up under an abstract Projectile class. Each subclass describes the projectile's model, number of bounces, speed, etc.
The problem I'm having is that each TankWeapon subclass is duplicating a lot of code around the area where they construct a new projectile, because they each construct a different class. I want to move this code into the base class, but I would have to somehow inject the projectile class itself that the weapon needs to construct. I know I could literally pass a Class to the base upon construction, but that feels like the wrong way to go.
And while we're at it I have another design problem: How can I make my AI classes aware of the projectile class as well? Their decisions will depend on properties of the projectile being fired, such as how many times they can bounce off walls. Both the AI and Weapon classes are being given a reference to the parent Tank upon injection.
Edit:
It seems like my original question was a bit confusing, so I'll post code. I already have the DI set up for my tank.
public class Tank : ISolidObject
{
public TankAI AISystem { get; private set; }
public TankWeapon Weapon { get; private set; }
public Tank(TankAI aiSystem, TankWeapon weapon)
{
this.AISystem = aiSystem;
this.AISystem.Tank = this;
this.Weapon = weapon;
this.Weapon.Tank = this;
}
}
public abstract class TankAI
{
public Tank Tank { get; set; }
public abstract void Think();
}
// TankAI implementations aren't important here
public abstract class TankWeapon
{
protected int maxShotsOnScreen, shotsOnScreen;
public Tank Tank { get; set; }
public virtual void Shoot()
{
shotsOnScreen++;
// I really want to put the projectile construction code in here
}
}
public class BulletWeapon : TankWeapon
{
public BulletWeapon()
{
this.maxShotsOnScreen = 5;
this.turnSpeed = 1;
}
public override void Shoot()
{
// here's my problem. Every weapon class duplicates this, because I can't put the projectile construction in the base weapon class.
if (shotsOnScreen >= maxShotsOnScreen) return;
base.Shoot();
// just create it, it will take care of the rest
double bx = Tank.X - Math.Sin(Tank.AngleTurret * Math.PI / 180.0);
double by = Tank.Y + Math.Cos(Tank.AngleTurret * Math.PI / 180.0);
// note that projectiles subscribe themselves to the game entity handler, so don't have to store it myself.
// this weapon creates bullets. A different weapon might create rockets. How would the base class know which? Is there any way I can prevent this code from being duplicated?
new Bullet(bx, by, Tank.AngleTurret).Death += ShotDeath;
}
private void ShotDeath(Projectile p)
{
p.Death -= ShotDeath;
shotsOnScreen--;
}
}
For the first question, it sounds like you need a ProjectileFactory
It would look something like
// somewhere in tank weapon's Fire method or whatever
Projectile p = projectileFactory.Create( myProjectile.GetType() );
For the second question, have the AI require injection of a Projectile or a Type
public Tank( TankAi ai, TankWeapon w) // ...
public TankWeapon( Tank t, Projectile p ) // ...
public TankAi( Tank t, Projectile p ) // ...
public TankAi( Tank t, Type projectileType ) // ...
A question for you...Why do the weapon and ai get references to the tank?
It sounds like you aren't using enough Interfaces. It helps to think about the distinction between behavior (implementation) and functionality (the exposed interface).
You want each projectile, AI, and weapon to function the same way (to have the same interface) but to implement unique behavior, with some shared behaviors. A typical model of such would be to have IWeapon, IProjectile, and IIntelligence interfaces which define the exposed public face of those objects. Then you'd have a base class of each (BaseProjectile, for example) that implements the interface, and provides some common behavior for all Projectiles to use.
Now in the constructor (or a setter, or whereever) on your classes, you take in an Interface to the type.
So AI_Tank_Boss class might look like
public class AI_Tank_Boss : BaseTank
public AI_Tank_Boss(IWeapon weapon, IInteligence ai)
{
this.Weapon = weapon;
this.AI = ai;
}
Now each of your tank methods that rely on an AI method (perhaps events that fire from the AI and the tank looks to those events to do something?) can be implemented to use the interface, and any weapon-specific code will call the IWeapon interface.
What actually happens is based on how the particular Weapon subclass implements the methods and how it uses the common code in the BaseWeapon. This is the basis of polymorphism and why injection works.
Passing a class to the base upon construction is indeed the wrong way to go. The base class should have no knowledge of its derived classes. If you "have to somehow inject the projectile class itself that the weapon needs to construct" it means you haven't designed your class hierarchy and methods properly.
Unless you post here and example of what you need to pass, it would be very difficult for me to provide a specific solution.

Categories