Inheritance and accessing different scripts with the same base one

Hi.

This is my first time using inheritance and I something bugging my mind: how do I access inherited scripts?

For context, here’s what I have: it’s an RPG game where there is a base “Combatant” script for every character involved in combat, including enemies. Each enemy inherits from the base Combatant script. E.g.

public class Player : Combatant
public class Wolf : Combatant

And then, there are methods such as this:

public void CauseDamage(Combatant target, Combatant caster, float spellDamage) { //stuff }

It deals with values such as magic attack, magic defense and HP, all declared and calculated in “Combatant”, hence it accessing the Combatant script for both target and caster. I’m not really sure how this will work out if, say, the player attacks the wolf, because neither of them will have the combatant script in them. Can Unity access inherited classes and treat them as their base class for their functions? If not, then what would the best alternative for this problem be?

Thanks in advance.

You can treat Player and Wolf as Combatant or as Player and Wolf, this is pretty much your decision and it depends on your needs.

You can for example have a list of Combatant where you can drop inside any kind of class that inherits from Combatant and use on all of them the functions declared in Combatant. This way you cannot access specific functions declared on child classes.
Or you can have a list of Player and a list of Wolf, and you can only drop inside Player and Wolf objects, but you can use specific functions that were declared on these classes.

    public class Combatant
    {
        public void CauseDamage ()
        {

        }
    }

    public class Player : Combatant
    {
        public void Move ()
        {

        }
    }

    public class Wolf : Combatant
    {
        public void Eat ()
        {

        }
    }

    public class Fights
    {
        void Fight ()
        {
            List<Combatant> combat = new List<Combatant>();
            combat.Add(new Player());
            combat.Add(new Wolf());
            combat.Add(new Combatant());
            for (int i = 0; i < combat.Count; i++)
            {
                // Can do
                combat[i].CauseDamage();

                // Cannot do
                combat[i].Move();
                combat[i].Eat();
            }


            List<Player> player = new List<Player>();
            player.Add(new Player());
            player.Add(new Player());
            player.Add(new Combatant()); // Cannot do
            player.Add(new Wolf()); // Cannot do
            for (int i = 0; i < player.Count; i++)
            {
                // Can do
                player[i].CauseDamage();
                player[i].Move();

                // Cannot do
                player[i].Eat();
            }
        }
    }

But you should have a look at C# interfaces, I would think it is better in a case like that.

1 Like

Unity can access inherited components by base class or by inherited easily. It works with interfaces too. I suggest you do not use inheritance for game logic and change to composition. Let them all have Combatant script on them. For player, add two scripts : Combatant + Player, for wolf, Cobatant + Wolf
This way you can create non-combatant wolves with same wolvish behaviour and with iheritance you cant.

A way to think about it once you’re getting the hung of it, is as if everything that would be written in Combatant you’d have it a the beggining of both your other scripts. So, if in Combatant you write int health =100; both the player and the Wolf will recognize this variable as if it was written above them. In fact in the inspector, in your Wolf component, you’lll see every Combatant variable, and after those, the once that are exclusive to the Wolf.
The same happens with methods. If you put a method in Combatant, both Wolf and Player have it right now, and you can call it as if it were declare in them.
If you have a method that only the player should have then write it in the Player script. If it’s shared by both, write it in the Combatant one.
If you have a Player myPlayer and you make this check if(this is Combatant) it’ll return true. The wolf script will do the same.
You can for example make a list List combatants; and Add your Wolf script and your Player script as if both were combatants scripts (they actually are, and again, they have everything that you’ve written in combatant in them).
Now, sometimes you’ll have a method that you want to have in both the Wolf and Player, let’s imagine for example a method TakeDamage(). Let’s make it simple: all it does is subrracting the health from the character

TakeDamage(int amount)
{ health -= amount}

Since you want both to have it, you put it in the Combatant class. But then you figure that when the player takes damage you also want blood to be splattered in the screen. You could put that to the method, but then when the Wolf gets damage it’ll also splatter the screen. A better approach could be to just go to the Combatante script and in TakeDamage() after what we wrote, add " if (this is Player){Splatter();}".
This will work, but then why use polymorphism? you could just as well put a bool isPlayer and forget about it.
Instead what you do in polymorphism is override TakeDamage().
So, you go to the Combatant script and in TakeDamage() you write the keyword “virtual”. This means that child classes can override the method {“ovewrite” = rewrite} it would look like this

//in your combatant class
public virtual void TakeDamage(int amount){
//The part of the method that will be shared between both Wolf and Player
}

your Wolf class don’t need any changes, since that method is fine for it. If you write inside of the Wolf class say at Start(){ TakeDamage(4);} it’ll take 4 points of damage.
Your Player class, on the other hand, you want to change a bit so what you do is this:

//override method in Player class

public override void TakeDamage(int amount)
{
//put this only if you want it take away the part of your health, if you don't put it, it'll just make the splatter and that'll be it.
base.TakeDamage(amount);

//Now, this part, only the player will have it:
Splatter();
}

Sometimes you want call the base class method. This is useful even though you don’t share a single line of code. Why? Imagine you have a list of Combatants that are in a fire trigger. They could be either Wolf or player, and they may react different to the explosion, but it’s useful to have a method that is share so you can do this

List<Combatants> combatantsInsideFire;
foreach (Combatant combatant in combatantsInsideFire)
{ combatant.ReactToFire();}

But in the cases were you don’t share any code you may consider to implement an interface instead, specially if you want to characters that react to fire that are not combatants, like bystenders for example.

3 Likes