OOP inheritance structure

Started by
16 comments, last by WozNZ 8 years, 6 months ago

You are pushing inheritance too far, The AI should be in a separate class. Composition is far better than inheritance in many cases.

Take AI for example, you might want to plug the same AI into a number of different NPCs, your tree get real complex if one is humanoid and the other is something else. Then ask yourself what the difference between a humanoid player and a humanoid NPC apart from how they receive their commands.

You classes should be more focused as this makes them easier to plug together. It sounds like you starting to experience the problems of trying to put too much into one class structure, a deep inheritance tree is a good indication of this. Take a step back and try to pull apart the concerns and wrap them in their own object. Also look at some of the abstractions you have in the tree, if they do not actually buy you anything drop them and flatten the tree.

You are also suggesting a structure I wrote above if I'm understanding it well? Anyway the AI of all the types is handled by the same state machine. A mob could start with a state that decides what to do next based on certain criteria then add that state to the stack of states in the state machine of that entity.

I could indeed create a seperate class for something like stats that handles a bit better then a regular map that needs to be looked up with strings.

I guess I need to do the same for items. Instead of Item->Wearable->Sword I can just have item with a String. But I need to handle a ton of cases with if statements this way.

Advertisement

You are pushing inheritance too far, The AI should be in a separate class. Composition is far better than inheritance in many cases.

Take AI for example, you might want to plug the same AI into a number of different NPCs, your tree get real complex if one is humanoid and the other is something else. Then ask yourself what the difference between a humanoid player and a humanoid NPC apart from how they receive their commands.

You classes should be more focused as this makes them easier to plug together. It sounds like you starting to experience the problems of trying to put too much into one class structure, a deep inheritance tree is a good indication of this. Take a step back and try to pull apart the concerns and wrap them in their own object. Also look at some of the abstractions you have in the tree, if they do not actually buy you anything drop them and flatten the tree.

You are also suggesting a structure I wrote above if I'm understanding it well? Anyway the AI of all the types is handled by the same state machine. A mob could start with a state that decides what to do next based on certain criteria then add that state to the stack of states in the state machine of that entity.

I could indeed create a seperate class for something like stats that handles a bit better then a regular map that needs to be looked up with strings.

I guess I need to do the same for items. Instead of Item->Wearable->Sword I can just have item with a String. But I need to handle a ton of cases with if statements this way.

I have done this sort of technique in the past and you do not need loads of if statements. I tend to give items/weapons attributes and flags and then use rules engines to determine if something is allow. You should be able to configure data files and not have to touch the code if done right.

So for items that can be carried you might have an attribute that indicates which slot it can be "worn" on. 0 = not wearable, 1 = head ..., 10 = single handed weapon, 11 = 2 handed etc

You now have a simple way to determine if an item is wearable, where it can be worn and all without layers in your class tree

To control how much a player/npc can carry you have a carry weight allowance, monsters like dogs might have carry allowance of 0kg which means they can't pick anything up. So again no need to have layers in class tree control this.

This are just examples, there are many ways to handle but as you can see when you start to use attributes and rules the need for high nesting of classes starts to fall away. It is also possible to construct the data such that you can load the rules from a file so you don't even need to change code to tweak rules and how things work.

I would still like to have a comment on the code I posted.

Then I guess I'm on the right pad by reading Avoiding the Blob since that is pretty much what I am creating, I document and structure it very well but it remains a blob.. Sometimes I am giving some code it's own place but it pretty much stays in the realm that many people call the blob. But the article about avoiding it stretches it far, I mean it has a separate component for health, this might be great for a asteroids clone but is it still common sense for "deeper" games?

Should I be creating a Interfaces / components for each and every stat or does it make more sense to combine it? I would say the latter but I still might be thinking in "blobs". Anyway while soaking up all the information regarding this I am presented with 4 options:

  1. Using Map<K, V> to store various data. This seems the most simplistic but is kinda error prone since I need to provide strings manually.
  2. Using a single interface Entity that holds a ton of things. This really seems wrong to me, yet the article uses it for the Asteroids game.
  3. Using a interface for each functionality. This could mean interface Health that implements the methods regenerate(int amount) and damage(int amount). But I still have to define these methods in the class it belongs too and thus generating a blob again. I could of course have these as normal classes instantiated within my creatures.
  4. A full component system like Unity uses. Where I use a component manager for each object and access these components by someObject.getComponent("componentX") but that seems like overkill for anything other then a engine to me.

So I guess I have to go with option 1, what was suggested earlier by Krohm. Or with 3 but make the "components" there own accessible classes from the entity holding it. I have been looking for examples but have not found any yet. If anyone can point me to some examples, preferably C# or Java, I would much appreciate it. For now I will prototype both options and play with the idea.

For your code sample I would say any time you end up with a whole raft of if statements like you have in AttackState it is a good indication something wrong.

Probably better to pass in the AttackState instance when you create the NPC instead of new it inside the class. This has a benefit in that it will decouple your NPC from how they do combat so if you decide one NPC, some form of "boss", will use a slightly different combat strategy that is now easy to do, you just give a different AttackState instance to it.

For the points

1 - The Map<K, V> is fine for attributes, such as strength, int etc. If some npc like a wolf does not need charisma then don't give it to them. If you only have a limited set of attributes and you know it will not change then use an enum or define a set of string constants for all the types so you are not passing in magic strings which is brittle and error prone.

2 - For something as simple as asteroids then the single entity probably works, there is little complexity.

3 - You have highlighted why component systems are popular

4 - You are creating a game engine, a single game focused one from the sounds of it but still an engine :)

There is no right way in the end, what works for you is what is right. Some routes will give you more outs when say you want to add a new feature when you never thought of before.


For your code sample I would say any time you end up with a whole raft of if statements like you have in AttackState it is a good indication something wrong.

That is the whole point of a Finite State Machine. It could be broken down in more separate states but in the end it checks for something and sets a state for it. The more variables have influence the more if statements are needed. But this is very extensible, all you have to do is create a state that you need and add it to the stack whenever you need it. A very simple C++ example from ai-junkie.com shows how it works.

When the state of the miner in the tutorial is set to dig, it first checks if it is at the mine. And if it isn't it moves the miner to the mine.

While updating the state the miner earns golden nuggets but increases fatique. It needs to check if the state of the player and handle accordingly, thirsty GoToSalloon or fatigued GoGomeAndRest and if the miners pockets are full of gold DepositGold.

Whenever I decide to change something. Let's say instead of bringing all the money to the bank keeping something in it's old sock that is easily changed in DepositGold. And whenever I decide the miner has a bladder I add that field to the miner, increase it each update of the miner and handle it with an if statement in the global state.

With a much more complicated game the states will grow big but should still be manageable since each state should handle a single task but checks many variables on how to proceed.


Probably better to pass in the AttackState instance when you create the NPC instead of new it inside the class. This has a benefit in that it will decouple your NPC from how they do combat so if you decide one NPC, some form of "boss", will use a slightly different combat strategy that is now easy to do, you just give a different AttackState instance to it.

Yeah obviously, some of my creatures enter the map with hunting, some with roaming and some just idling until they see the player. And each is it's own state.


3 - You have highlighted why component systems are popular

,>

Did I? The interface method or the class method? I have come across interface methods but that would mean having a ridiculous amount of interface implementations for my creature class and generating a huge blob class.


For your code sample I would say any time you end up with a whole raft of if statements like you have in AttackState it is a good indication something wrong.

That is the whole point of a Finite State Machine. It could be broken down in more separate states but in the end it checks for something and sets a state for it. The more variables have influence the more if statements are needed. But this is very extensible, all you have to do is create a state that you need and add it to the stack whenever you need it. A very simple C++ example from ai-junkie.com shows how it works.

When the state of the miner in the tutorial is set to dig, it first checks if it is at the mine. And if it isn't it moves the miner to the mine.

While updating the state the miner earns golden nuggets but increases fatique. It needs to check if the state of the player and handle accordingly, thirsty GoToSalloon or fatigued GoGomeAndRest and if the miners pockets are full of gold DepositGold.

Whenever I decide to change something. Let's say instead of bringing all the money to the bank keeping something in it's old sock that is easily changed in DepositGold. And whenever I decide the miner has a bladder I add that field to the miner, increase it each update of the miner and handle it with an if statement in the global state.

With a much more complicated game the states will grow big but should still be manageable since each state should handle a single task but checks many variables on how to proceed.

What I meant here is that you have all the state machines for what you have called divisions in one state machine, that is a lot of noise. Unless the NPCs can switch division where it becomes important, otherwise it is extra control flow that in reality is never used. The link you provided has this where the state classes in the example are the Troll states, not all NPC types in one place. If you has 20+ divisions then you can see that would get long and for NPCs of the last division type that would have to walk a long if chain to get to the right place. Having the right single focused state engine for the division saves all that work each time :)


3 - You have highlighted why component systems are popular

,>

Did I? The interface method or the class method? I have come across interface methods but that would mean having a ridiculous amount of interface implementations for my creature class and generating a huge blob class.

What I meant there is that your point 3 had already identified that approach would lead you back into massive blob classes again which is why component systems are popular as they avoid it.

With components, there is not the need for complex component lookup systems like unity, you could go simple If there are not a mass of components. Just have references to components in the Entity like this... (C# but much the same)

public class Entity

{

public StateEngine State {get; set;}

public IDictionary<string, int> Attributes {get; set;}

public IHealth Health {get; set;}

public IBackpack Backpack {get; set}

}

You only set the various parts if they apply, a wolf would not have a backpack to carry stuff so would be null. So it is easy to slap entities together from parts without the need of complex object trees. Whatever factory you used to create an NPC would pick the right parts and slot them together.

As always there are many ways to handle your entity composition, it is just finding the right path for you and normally driven by how complex your game mechanics are smile.png

Ahh yes I get what you mean. But what do you suggest? I am thinking of MeleeAttackState, RangedAttackState, SpellAttackState, etc now. So for example a spell like berserk can set a mage to a MeleeAttackState. Or should I have a MageAttackState and check in there if the mage has a Berserk flag? The latter creates a lot of statements again, so I guess I know the answer.

I like to thank you, along with your input and what I have been reading I learned a lot. I now have a class DrainableStat where I create a health, mana and energy object from. It handles everything like adding and subtracting as well as bonus modifiers to it. This is how I create a creature now:


CreatureData race = new CreatureData("Dwarf", 10, 10, 10, 10, 10, 10, 10);
CreatureData career = new CreatureData("Warrior", 2, 2, 0, -2, 0, 0, 0);
new Creature(race, career);

Deciding on how I want to implement abilities since these should be racial or career restricted. I guess I plug them in manually and provide options to the player based on there race and career.

For how you split the states the real question is how different is the code. I normally start creating code that I think is the right sort of shape then review what I have and reduce it down, look for what is common and abstract it. I might end up rewriting stuff a bunch of times, each time more concise and compact than the last.

You might find that for your game that a spell is just a ranged attack. You might also find that the code is similar enough that a Melee attack is just a range attack with very short max range. If all this is true you might just end up with attack and some parameters to configure smile.png

Never be afraid to rewrite and refactor with hindsight. The less code you can write to reach your goal the better. The only trouble with writing less code is it take longer smile.png

Code is a life long learning process, the more you try new things and ways of looking at problems the better you get. I look back at some of my old code now and shake my head at my thought processes back then :)

Glad I could help

This topic is closed to new replies.

Advertisement