0

I am trying to minimize the amount of duplicate code my project has by using inheritance.

For example:

Every Character in my game whether they are Player, Enemy, Or NPC all have a member field HP.

They all also have a function to TakeDamage which effect HP.

In my game, this function is very elaborate comparing the attacker's stats, the attacking weapon stats against the attackee's stats and their equipment.

This function is adjusted frequently to get it so it feels right and I don't want it duplicated on the npc, enemy and player controller, and let us just imagine one-day "networkPlayer" too because hey, a guy can dream!

There are many more functions like this that are the same too and I really would like to have them all in one CharacterController class that PlayerController, EnemyController, etc inherit from.

Okay, so, is this the proper usage of inheritance?

There is a second layer here too.

My players, enemies and npcs are all built from an online Database that changes as new enemies and players are added.

So I have a CharacterBase class that builds the shared variables between characters like HP, Stats, Name, etc...

Then there is the derived classes like EnemyBase (inherits name, hp, stats from Character base) but extends upon this class with its own variables such as Rarety, MaxGoldHeMayCarry, XPGainedIfYouKillMe, etc...

NOW THE PROBLEM IS:

EnemyBase which extends CharacterBase Has two copies of HP! In the inspector it shows up as CharacterBase->HP and EnemyBase->HP

And when I call CharacterController.HP minus one it subtracts one from CharcterBase HP but there is this untouched EnemyBase HP!

This was really wordy. I am sorry. The following is some test code to demonstrate

public class TestingInheritance : MonoBehaviour
{
    // set this to an empty prefab in the inspector
    public GameObject TestObjectPrefab;

    private void Start()
    {
        GameObject TestPlayer = Instantiate(TestObjectPrefab);

        PlayerController pController = TestPlayer.AddComponent<PlayerController>();
        pController.TakeDamage();   //calls the overridden version of TakeDamage() (which is good)


        GameObject TestEnemy = Instantiate(TestObjectPrefab);

        EnemyController eController = TestEnemy.AddComponent<EnemyController>();
        eController.TakeDamage();   //calls the overrideden version in EnemyController (which is good)

    }
}

[System.Serializable]   //comes from db
public class CharacterBase
{
    public int HP=100;
}

[System.Serializable]   //comes from db
public class Enemy : CharacterBase
{
    public string Name = "Enemy";
}

[System.Serializable]   //comes from db
public class Player : CharacterBase
{
    public string Name = "Player";
}

public class CharacterController : MonoBehaviour
{

    //I want to be able to access the variables that all characters share in common, such as HP, Name, Stats, Inventory
    public CharacterBase characterBase = new CharacterBase();

    /// <summary>
    /// Reduces HP by 1
    /// TakeDamage() in my real game is actually much more complicated and I dont want to duplicate the code in Enemy, Player, and NPC 
    /// there are many more functions that I want to be able to call for any kind of character such as UseItem(potion), ChangeState(poisoned), Die()
    /// All these functions share a ton of the same code and I dont want to duplicate it, especially because it changes often during my development
    /// </summary>
    public virtual void TakeDamage()
    {
        //here we just loose one hp
        characterBase.HP -= 1;
    }

}

public class PlayerController : CharacterController
{
    public Player playerBase = new Player();

    public override void TakeDamage()
    {
        //do the code that is shared for all Characters
        base.TakeDamage();

        Debug.Log(playerBase.Name + " was hit!--- HP now: " + playerBase.HP);
        //Prints Player was hit! --- HP now: 100 and the inspector shows CharacterBase >> HP 99 AND PlayerBase >> HP: 100, Name: Player

        //IF WE CHANGE THE ABOVE LINE TO SAY
        Debug.Log(playerBase.Name + " was hit!--- HP now: " + characterBase.HP); 
        //Now we print 99, which is good. but we still have both CharacterBase >> HP 99
        //AND the Wrong, ugly, bad, confusing PlayerBase >> HP: 100

    }
}


public class EnemyController : CharacterController
{
    public Enemy enemyBase = new Enemy();

    public override void TakeDamage()
    {
        base.TakeDamage();
        Debug.Log(enemyBase.Name + " was hit! --- HP now: " + enemyBase.HP);
        //Prints Enemy was hit! --- HP now: 100 and the inspector shows CharacterBase >> HP 99 AND EnemyBase >> HP: 100, Name: Enemy
    }
}

what the inpsector show

So, how do I restructure this so it only has one variable HP that both CharacterController and PlayerController access?

I am sorry this was so long, is anyone still reading? I dont know how to make this anymore concise...

7
  • I'm pretty sure you did not post actual code - your actual code likely has public int HP=100; in Enemy class too... (the sample shown here has really no way to behave the way you described) Commented Sep 9, 2019 at 23:29
  • @AlexeiLevenkov thanks for reading! It does not have public int HP=100; this is copied and pasted. I think the line public Enemy enemyBase = new Enemy(); creates the duplicate HP variable. ...I think it is weird too Commented Sep 9, 2019 at 23:32
  • Ignore previous comment - I thought you just talking about characterBase.HP -= 1; behavior wrong (which obviously not going to happen with code shown)... but instead you are expecting some magic in base controller to discover new properties from derived class... instead of using the same property... Indeed code you've shown is demonstrating error completely, you just have a lot of unrelated staff about serialization. Commented Sep 9, 2019 at 23:36
  • right on, the serialization is necessary as the those classes (Enemy, Player, etc) are generated from reading a json created by a DB. Is it the serialization that is causing me problems? Commented Sep 9, 2019 at 23:38
  • Also, I dont want the base class to magically discover the new property. I do NOT want the new property at all!!! I want the derived class to use the base class property Commented Sep 9, 2019 at 23:46

2 Answers 2

4

why is this here?

public CharacterBase characterBase = new CharacterBase();

All your classes that inherit from it will call it separately from their own code, (eg. public Enemy enemyBase = new Enemy();).

That's why you're seeing both. You have two separate stats for every controller as a result. If you want to be able to access it, you need to create a get method in the character controller:

public CharacterBase stats { get; protected set; }

And then in its subclasses you populates that field with a value.

Sign up to request clarification or add additional context in comments.

Comments

0

Thanks Draco18s your answer made it work.

In order to avoid the duplicate fields we definitely need to get rid of:

public CharacterBase characterBase = new CharacterBase();

But we still need a reference to the characterBase so I added:

    protected CharacterBase characterBase;
    public void Init(CharacterBase _characterBase)
    {
        characterBase = _characterBase;
    }

now Start() looks like this

private void Start()
    {
        GameObject TestPlayer = Instantiate(TestObjectPrefab);
        TestPlayer.name = "p";

        PlayerController pController = TestPlayer.AddComponent<PlayerController>();
        pController.Init(pController.playerBase);
        pController.TakeDamage();  
    }

That does it!! no more duplicate fields! And proper output in Debug.Log - Thanks Guys!

3 Comments

You might wanna take a look at this: google.com/url?sa=t&source=web&rct=j&url=https://…
@Menyus thats a good article on inheritance. My issue came when things got mudldled using an inherted controller class. I am wondering this morning if this is a normal use case? are other people using inheritance not on "objects" but on "controllers"?
The reason i sent you the link because you are using a direct reference to your base class but i wanted to show you that you can access base class methods and variables using the base keyword

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.