Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#, Unity - Single function taking multiple different objects

I am in need of your help. I am in the middle of arranging a script that can check various conditions before an ability can be executed in a RPG game.

All these abilities are in individual classes (Fireball, Heal, Poison) all derived from another abstract class (Ranged ability, Healing ability, DOT ability), which all are parented to an abstract class (Ability).

In order to avoid creating multiple functions, to handle every single ability:

void condition(Fireball f){//test}; 
void condition(Heal f){//test}; 
void condition(Poison f){//test};

I am trying to create a single function call that can take all types of abilities.

void condition(Ability f){//test}

So far I have succeded in creating a Fireball object and pass it to the function.

Fireball _fire = new FireBall();
condition(_fire);

void condition(Ability f){//test}

From here I can access all the public variables initialized in the Ability class, but I can't access the public variables initialized in the derived classes (Ranged ability, Healing ability, DOT ability).

Is it me who is forgetting something, or am I looking at this at a wrong perspective? (I am not great at utilizing inheritance and abstract classes.)

like image 315
Marc Pilgaard Avatar asked Jan 10 '13 22:01

Marc Pilgaard


1 Answers

Without knowing more details of what the condition function does, you have two options.

One, you can do something like

if (f.GetType() == typeof(FireBall))
{
  fireBall = (FireBall)f;
  fireBall.FireTheFireBall();
}
else if (f.GetType() == typeof(Heal))
...

Or, your Ability can have an abstract Activate method, which all derived classes are required to overload:

class Fireball
{
   public override void Activate()
   {
       //do fireball specific things
       this.FireTheFireBall();
   }   

   public void FireTheFireBall() {...}    
}

class Heal
{
   public override void Activate()
   {
       //do healing specific things
       this.ApplyTheBandage();
   }
   ...
}

abstract class Ability
{
  public abstract void Activate();
}

void condition(Ability f){
   f.Activate(); //runs the version of Activate of the derived class
}

Then any thing that works with an Ability can call someAbility.Activate() and the implementation provided by the derived class will get executed.

You should also study up on interfaces, which are kind of like abstract classes. The benefit of interfaces is you can implement multiple of them, whereas you are limited to inheriting from only one base abstract class. Think about a IKnob interface that has Turn and Pull functions. You might have a Drawer class that implements IKnob, a Door class, a TrappedDoor class which implements Turn and activates a trap. A Player walks up to a door, and hits the Use button on it, and you pass to the open function the object, Open(IKnob knob)

void Open(IKnob knob)
{
   knob.Turn();
   knob.Pull();
}

class TrappedDoor:IKnob,IMaterial,ISomethingElse,IHaveTheseOtherCapabilitiesAsWell
{
  private bool TrapAlreadySprung{get;set;}
  //more complex properties would allow traps to be attached either to the knob, or the door, such that in one case turning the knob activates the trap, and in the other, Pull activates the trap
  public Turn() { 
    if(! TrapAlreadySprung)
    {
      MessageBox("You hit your head, now you're dead");
    }
  }
}

There's ways to check if something has an interface, so if some a player walks up to an item and tries to talk to it you can check if the object has the ICanTalk interface, if it does then call object.GetReply("Hello") and the object can respond. So you can have talking doors and rocks if you so desire. You get all your code that handles talking to things/displaying responses etc. working with ICanTalk interface methods, and then other classes can implement ICanTalk and they each decide how they respond to be talked to. This concept is known as "seperation of concerns" and helps you create more reusable code.

The important thing is you can write a piece of code, an algorithm, function, etc, that only works with that interface, and that way once you get that code working with the interface, you can then use that interface on any class, and that class can leverage the prexisting code.

I.e. your condition function, if it took in an IAbility interface, once you have that code working, then any class you create that implements IAbility can be passed to the condition function. The condition function is in charge of doing whatever it's supposed to, and the class implementing IAbility takes care of whatever is specific to it inside of the methods it implemented.

Of course the classes implementing the abstract class or interface must implement the methods required, so sometimes you might feel like you are duplicating code. For example, if you have similar classes, like TrappedDoor and Door, a TrappedDoor might behave just like a regular Door if the trap is not set/already sprung. So you might either inherit from Door in this case, or have a private Door property(known as "composition"). If the trap is already sprung, then you can call into the base Door class or private Door property and call .Turn so that you just reuse the default behavior of a regular door in the case that the trap isn't active.

Test if object implements interface

Personally I mostly use interfaces and composition, instead of inheritance. Not that inheritance it terrible, but inheritance hierarchies can quickly become very complicated.

like image 184
AaronLS Avatar answered Oct 14 '22 22:10

AaronLS