Search Unity

C# noob question: Derived classes in method parameter

Discussion in 'Scripting' started by pixpusher2, Apr 25, 2015.

  1. pixpusher2

    pixpusher2

    Joined:
    Oct 30, 2013
    Posts:
    121
    Hi all, I'm still pretty new to programming.
    In the example below, I have a base class called Entity, which is then inherited by 3 other classes, Knight, Goblin and Rock.

    Then in another script, I have a ReduceHealth() method that takes in Entity as parameter. Now the issue I'm having is I'll have to recast each Entity that gets passed into the method in order to access their Health variable, which in turn creates a lot of duplicate codes.

    Health wasn't declared in the base class because the Rock doesn't need one.

    Is there a way to use a generic code that lets me handle this more efficiently? (see example near end of code)
    Thanks!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;    // required for List
    4.  
    5. // Base class
    6. public abstract class Entity
    7. {
    8.     protected string id = "None";
    9.  
    10.     // Accessor
    11.     public string ID { get; set;}
    12. }
    13.  
    14.  
    15. // Derived classes
    16. public class Knight : Entity
    17. {
    18.     protected int health;
    19.  
    20.     // Constructor
    21.     public Knight()
    22.     {
    23.         this.ID = "Knight";
    24.         Health = 3;
    25.     }
    26.  
    27.     // Accessor
    28.     public int Health { get; set; }
    29. }
    30.  
    31. public class Goblin : Entity
    32. {
    33.     protected int health;
    34.  
    35.     // Constructor
    36.     public Goblin()
    37.     {
    38.         this.ID = "Goblin";
    39.         Health = 1;
    40.     }
    41.  
    42.     // Accessor
    43.     public int Health { get; set; }
    44. }
    45.  
    46. public class Rock : Entity
    47. {
    48.     // I am a Rock. I can't die!
    49.  
    50.     // Constructor
    51.     public Rock()
    52.     {
    53.         this.ID = "Rock";
    54.     }
    55. }
    56.  
    57.  
    58. // Code testing class
    59. public class ClassTest : MonoBehaviour
    60. {
    61.     public List<Entity> characterList = new List<Entity>();
    62.  
    63.     void Start()
    64.     {
    65.         Entity knight = new Knight();
    66.         Entity goblin = new Goblin();
    67.         characterList.Add(knight);
    68.         characterList.Add(goblin);
    69.  
    70.         for (int i = 0; i < characterList.Count; i++)
    71.         {
    72.             ReduceHealth(characterList[i]);
    73.         }
    74.     }
    75.  
    76.     public void ReduceHealth(Entity who)
    77.     {
    78.         if (who is Knight)
    79.         {
    80.             // Recast it as Knight
    81.             Knight k = who as Knight;
    82.             k.Health--;
    83.             Debug.Log(k.ID + "'s HP: " + k.Health);
    84.         }
    85.         else if (who is Goblin)
    86.         {
    87.             // Recast it as Goblin
    88.             Goblin g = who as Goblin;
    89.             g.Health--;
    90.             Debug.Log(g.ID + "'s HP: " + g.Health);
    91.         }
    92.  
    93.         // Generic code. How can I do this??
    94. //       if (who is Rock)
    95. //              return;
    96. //
    97. //        who.Health--;
    98. //        Debug.Log(who.ID + "'s HP: " + who.Health);
    99.     }
    100. }
     
  2. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Multiple approaches.
    1. Put the health on the base class. Ignore it on the rook. (Could be a bad idea)
    2. Use reflection to access it. (Probably a bad idea)
    3. Abandon inheritance and use component based design (Good idea).
     
    pixpusher2 and Korno like this.
  3. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    Put your health variable in the entity class. Doesn't matter that the rock doesn't need it
     
    pixpusher2 likes this.
  4. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    There are a couple of approaches to this. You can extend your inheritance chain to look like this:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;    // required for List
    4. // Base class
    5. public abstract class Entity
    6. {
    7.     protected string id = "None";
    8.     // Accessor
    9.     public string ID { get; set;}
    10. }
    11.  
    12. public abstract class LivingEntity : Entity
    13. {
    14. protected int health;
    15. public int Health {get; set;}
    16. }
    17.  
    18. // Derived classes
    19. public class Knight : LivingEntity
    20. {
    21.     protected int health;
    22.     // Constructor
    23.     public Knight()
    24.     {
    25.         this.ID = "Knight";
    26.         Health = 3;
    27.     }
    28.     // Accessor
    29.     public int Health { get; set; }
    30. }
    31. ..
    32.  
    33. void DamageEntity(LivingEntity e, int damage)
    34. {
    35. e.Health -= damage;
    36. }
    37.  
    38.  
    Or use component based design. It can help avoid a huge inheritance chain that sometimes happens in situations like this. This is how unity works (you add components to a game object).

    The best solution is often a mix between component based design and inheritance.

    Remember inheritance means "is a" and component based design means "has a"
     
    pixpusher2, Ryiah and Kiwasi like this.
  5. pixpusher2

    pixpusher2

    Joined:
    Oct 30, 2013
    Posts:
    121
    Thanks everyone! I can see how Inheritance can get messy down the line with all the chaining.

    I've read up on Component based design and understood how it works generally, but the implementation is a bit over my head at the moment. I'll look up a few more articles and see how it goes!

    If that doesn't pan out, I'll just put the variables in Base class.

    Edit:
    I think I get it now! The Composition samples over here helped: http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
     
    Last edited: Apr 25, 2015