Jump to content
  • Advertisement
Sign in to follow this  
menyo

OOP inheritance structure

This topic is 1068 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm having some issues with my inheritance structure.

 

I'm having a abstract Class Entity where all characters are derived from. A player can be a Mage -> Humanoid -> Entity for example. But AI/NPC needs to extend an extra Class NpcEntity. 

 

I have many abstractions of Entity so either I have to create a AI class for each one. Or have methods not relevant to the player in the Entity class. Neither of these options seem good to me.

class mageMob extends HumanoidMob
class HumanoidMob extends NpcEntity
class NpcEntity extends Entity

Things go even deeper then the above example and I have a lot of different character classes that need to behave differently.

 

The reason for this is that I want to implement a State Machine for AI. A player does not need all the methods and fields for that. So how would I do this in Java? I really do not want to change out the classes for something like an enum and have a ton statements everywhere to check what enum the object is.

Edited by menyo

Share this post


Link to post
Share on other sites
Advertisement

My first thought is why does NPC Entity have to be it's own class? Is it any different than any other entity?

 

A better idea would be to have a controller class, which is a member of entity. By extending the controller into Player Controller and AI controller, We can swap out control of that entity simply by switching it's controller. The player controller reads input, and applies it to the entity. An AI controller, which could be a state machine  or any other AI technique you want to use(behavior tree, etc) would evaluate the state of the game and decide what the entity should do.

 

By creating different instances of player controller, you can have different characters respond differently to player input, and the same thing with the AI controllers. This clears up entity so that it is agnostic as far as who or how it is controlled, and keeps AI and player code separate as well.

Share this post


Link to post
Share on other sites

Thats the usual problem with large inheritance trees.

You better have a separate AI object, which gets a reference to the Entity, then calls its methods to control it. You don't need to create one AI class for each object, as they should provide the same API.

Other parts you can cut out of the Entities and have them contain subcomponents which handle certain things or try not to use new classes when you can just have some data.

Share this post


Link to post
Share on other sites

Another problem this thing has is the following:

entity is a 'engine' notion. You use it to somehow group related data.

 

"Being a mage" is a gameplay-related notion. You're probably using this difference in logic to provide (override) logic to select spells to cast etc. A better way would be to provide entities with 'magic abilities' (gameplay term) a SpellSelector object which will decide which spell to cast at a given time.

 

With "being humanoid" that's even more subtle: in theory I could just change the asset of the monster and draw it an humanoid. Everyone would agree it looks like an humanoid. Ok, you probably use this to determine which spells affect entity x, as "lock humanoids" doesn't work on non-humanoids. For that purpose, you just need a boolean flag to go along your entity and that would be a data-driven design.

Share this post


Link to post
Share on other sites

My first thought is why does NPC Entity have to be it's own class? Is it any different than any other entity?

 

A better idea would be to have a controller class, which is a member of entity. By extending the controller into Player Controller and AI controller, We can swap out control of that entity simply by switching it's controller. The player controller reads input, and applies it to the entity. An AI controller, which could be a state machine  or any other AI technique you want to use(behavior tree, etc) would evaluate the state of the game and decide what the entity should do.

 

By creating different instances of player controller, you can have different characters respond differently to player input, and the same thing with the AI controllers. This clears up entity so that it is agnostic as far as who or how it is controlled, and keeps AI and player code separate as well.

 

 

Thats the usual problem with large inheritance trees.

You better have a separate AI object, which gets a reference to the Entity, then calls its methods to control it. You don't need to create one AI class for each object, as they should provide the same API.

Other parts you can cut out of the Entities and have them contain subcomponents which handle certain things or try not to use new classes when you can just have some data.

 

Entity just holds all basic information and functionality for each character. From a name and position to moving around on the map.

 

I have always did it this way but never actually had to deal with the issue I'm dealing with now. I can see that I now need a player class with the functionality of the player and a class with the functionality of AI. Both can still inherit from Entity since they should inherit basic things like movement, position, name, etc. I also need to put in things like attack(Entity attacker, Entity defender); otherwise I would need double methods and if statements to have mobs attack eachother too.

 

The race and type classes can be held as a reference inside a player and NPC.

 

All sounds very logically now. I have some changes to make and hopefully more copy/paste then delete and rewrite :D.

Share this post


Link to post
Share on other sites

Another problem this thing has is the following:

entity is a 'engine' notion. You use it to somehow group related data.

 

"Being a mage" is a gameplay-related notion. You're probably using this difference in logic to provide (override) logic to select spells to cast etc. A better way would be to provide entities with 'magic abilities' (gameplay term) a SpellSelector object which will decide which spell to cast at a given time.

 

With "being humanoid" that's even more subtle: in theory I could just change the asset of the monster and draw it an humanoid. Everyone would agree it looks like an humanoid. Ok, you probably use this to determine which spells affect entity x, as "lock humanoids" doesn't work on non-humanoids. For that purpose, you just need a boolean flag to go along your entity and that would be a data-driven design.

 

Well, the idea was that a humanoid is a lot more different then say, a animal or undead with really it's own functionality. They would have additional stats like charisma, can conversate with the player and other humanoids and are able to use tools and wield armor.

 

I have always used this with success but for much simpler concepts.

Share this post


Link to post
Share on other sites


Another problem this thing has is the following:
entity is a 'engine' notion. You use it to somehow group related data.

 

What naming do you suggest for every moving character in the game since Character is also taken? I always thought engines used the term Entity for the same reason but in the case of unity it's a entity component system. Anyway, does it really matter I'm using the word Entity for what I am doing? I am not using Unity or anything that uses Entity and might be conflicting.

Share this post


Link to post
Share on other sites

I don't think you got the point. The point is you shouldn't mix notions from different levels of abstractions. If you do, you probably can take the chance to refactor and thing in layered abstracted components.

 

The use of the entity word is mostly historical. In my engine, entities are just called Object because if you think at it, you cannot define a single action common to all entities besides existing. Feel free to use that term if it fits your mindset, it seems you're fully into the entity mindset.

 

 

 


Well, the idea was that a humanoid is a lot more different then say, a animal or undead with really it's own functionality. They would have additional stats like charisma, can conversate with the player and other humanoids and are able to use tools and wield armor.
 
I have always used this with success but for much simpler concepts.

Most of which are not something you should deal with code. Conversations are scripted anyway, just give the objects a ConversationTree object... or not. Gameplay stats are most likely not a performance path: cut a few corners and just put them in a std::map<string, uint>.

Want to limit use of tools and armor? Just have an string race.

All of those are data-driven and provide nearly equivalent functionality. 

 

Note: std::map<string, uint> is an associative container. You ask a name and it gives you an hint. I don't know how it is called in Java. Sometimes they are called 'keyed arrays' or something along that.

Edited by Krohm

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

I don't think you got the point. The point is you shouldn't mix notions from different levels of abstractions. If you do, you probably can take the chance to refactor and thing in layered abstracted components.

 

The use of the entity word is mostly historical. In my engine, entities are just called Object because if you think at it, you cannot define a single action common to all entities besides existing. Feel free to use that term if it fits your mindset, it seems you're fully into the entity mindset.

 

 

 


Well, the idea was that a humanoid is a lot more different then say, a animal or undead with really it's own functionality. They would have additional stats like charisma, can conversate with the player and other humanoids and are able to use tools and wield armor.
 
I have always used this with success but for much simpler concepts.

Most of which are not something you should deal with code. Conversations are scripted anyway, just give the objects a ConversationTree object... or not. Gameplay stats are most likely not a performance path: cut a few corners and just put them in a std::map<string, uint>.

Want to limit use of tools and armor? Just have an string race.

All of those are data-driven and provide nearly equivalent functionality. 

 

Note: std::map<string, uint> is an associative container. You ask a name and it gives you an hint. I don't know how it is called in Java. Sometimes they are called 'keyed arrays' or something along that.

 

This requires a bit different thinking. Could you elaborate some more on it? Does this look more like what you suggest?

 

public class Creature {
    String name;
    String race;
    String division; //could not come up with something better to replace reserved "class"
    HashMap<String, Integer> stats = new HashMap<String, Integer>();
    HashMap<String, Ability> abilities = new HashMap<String, Ability>();

    public Creature(String name, String race, String division) {
        this.name = name;
        this.race = race;
        this.division = division;

        //Add stats and abilities to maps
    }
}

public class Player extends Creature
{
    public Player(String name, String race, String division) {
        super(name, race, division);
    }
    
    private void attack(Creature target)
    {
        target.stats.put("currentHealth", target.stats.get("currentHealth") - stats.get("damage"));
    }
    
    //Add player controls    
}

public class Npc extends Creature
{
    
    StateMachine stateMachine = new StateMachine();
    
    public Npc(String name, String race, String division) {
        super(name, race, division);
        
        setState();
    }
    
    private void setState()
    {
        if (division.equals("mage"))
            stateMachine.addState(new AttackState(this, target));
    }
}


public class AttackState extends State{
    Creature self;
    Creature target;

    public AttackState(Creature self, Creature target) {
        this.self = self;
        this.target = target;
    }

    @Override
    public void enter() {
    }


    @Override
    public void exit() {
    }

    @Override
    public void update() {
        if (self.getDivision().equals("mage"))
        {
            //check mana
            
            //check spellbook
            
            //check observations in StateMachine
            
            //choose something logical to fire at a target
            
            //etc...
        }
        else if (self.getDivision().equals("warrior"))
        {
            //if low on health flee
            
            //check distance from player
            
            //if to far add a state on top of this to close down
            
            //if in range of available ability use it.
            
            //etc..
        }
    }
}
Edited by menyo

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!