Multiple inheritance and Game Objects

Started by
7 comments, last by stevemkrenz 16 years, 4 months ago
Hello, Can I run this past you? Here's how I am planning on making game objects, using Multiple inheritence: Base classes: BaseObject SelectableObject IntelligentObject MobileObject ContainerObject Derived objects: Player ship: Base + Selectable + Mobile + Container NPC ship: Base + Selectable + Intelligent + Mobile + Container Land Mounted Turret: Base + Selectable + Intelligent Boring Rock: Base Resource: Base + Selectable + Container etc I hope that is pretty self-explanatory. Does this approach make sense? What pitfalls should I expect? Many Thanks Si
Advertisement
This is exactly what I tried to do. The problem is that you have to tediously write a bunch of stuff that can be derived from a number of simple explicit and implicit parameters, or in other words, you have to do something that the computer can do. For example, each time you make a most-derived class (in your example "Player ship", "NPC ship", and so on), you need to write a constructor that calls the constructors of its parent classes.

So if your language is fully reflective or has a good code generator available, I'd say go for it. Otherwise you're going to have to find another good way (which I haven't found yet, despite exploring a bunch of possibilities).
Should I try to translate this into a more nested approach?

eg:

baseObject |                      | StaticObject           MobileObject|        |             |                |Dormant  NPC           Player           NPC


This looks so much harder to implement! SO much more work due to myriad combinations!
Don't nest it. What you want to do is change those into components, and have your base object have a group of components which are selectable, intelligent, mobile, or container. Look into Composition rather then Hierarchical.
^ Which, unless you want to make your own dispatch mechanism (no doubt complex and/or with hefty maintenance), creates greater dependency between (component) classes, and is almost certainly slower than straight polymorphism.

EDIT- Oh yeah, I do agree that you shouldn't use a traditional hierarchy.
In my opinion, the separation of player and npc into different classes is often a mistake. A human player and an AI controller both control ships, and a lot of systems will care about which ship the player is controlling, but the ship itself is pretty much the same regardless of whether it is player or AI controlled.

If you think in terms of components, the inheritance hierarchy of the 90's starts to look really pointless. Why do static and mobile objects need to be different classes? A message dispatch system isn't really significantly slower than trying to accomplish the same thing with polymorphism, and it can reduce the amount of redundant classes needed to accomplish things.
I'll forward two interesting discussions about aggregation/components/data driven designs, and deep hierarchies.

clicky

a shorter one with the links
clicky 2

Mind you, there are loads of ways to do things, and tons of articles relating to that sort of design. In fact, there are whole chapters in the Game Programming Gems and design pattern books dedicated to aggregate, component designs.

so in the end instead of looking like

class ShipPlayer: public ShipBase, public Selectable, public Mobile, public Container{};class ShipNPC: public ShipBase, public Selectable, public public Intelligent, public Mobile, public Container{};class TurretLandMounted: class TurretBase, class Selectable, class Intelligent{}class BoringRock: public Base{}class Resource: public Base, public Selectable, public Container{};


you would have

class Component{};class ContextSensitive: public Component{};class Physics: public Component{};class Container: public Component{};class Controller: public Component{};class Ship: public Entity{  ContextSensitive*   m_context;     // selectable, not selectable  Physics*            m_body;        // mobile, or static, or whatever  Container*          m_item;        // container item  Controller*         m_controller;  // player controls or AI (intelligence).};class Turret: public Entity{   ContextSensitive*   m_context;     // selectable, not selectable   Physics*            m_body;        // mobile, or static, or whatever   Container*          m_item;        // container item   Controller*         m_controller;  // player controls or AI (intelligence), scripted path.};class BoringRock: public Entity{   ContextSensitive*   m_context;     // selectable, not selectable, pickup   Physics*            m_body;        // mobile, or static, or whatever   Container*          m_item;        // container item};class Resource: public Entity{   ContextSensitive*   m_context;     // selectable, not selectable   Container*          m_item;        // container item};


All objects look very similar, so you could even make due to separating them into different entities, and have a way to differentiate them from the core entity base class.

In the end, you would have

enum ComponentClass{    ComponentClass_Context, // contextual info (what differentiate a 'rock' from a 'ship').    ComponentClass_Physics,    ComponentClass_Controller,    ComponentClass_SceneNode,    ComponentClass_Container,    ComponentClass_Script, // interface to script commands};class Component{   ComponentClass m_Class;};class Context: public Component{};class Physics: public Component{};class SceneNode: public Component // render information (model, anims, ...).{};class Container: public Component{};class Controller: public Component{};class Entity{    list<Component*> m_Components;    Component* getCompoment(ComponentClass component);};


There are many advantages, but also drawbacks. Making components talk to each other can be a pain (Physics would be interfaced by many components, notably the controller and SceneNode). but the advantages are not to be sniffed at. For example, when it comes to create objects from a script file, your EntityFactory and ComponentFactory are just script processors. Also, you can control the functionality of your objects via script commands through the controller component. It adds a lot of flexibility. You could have a talking rock for example :)

As for coding, it's great for re-factoring and re-using code. Some with more experience will chime in and correct me.

Everything is better with Metal.

Thanks, thats really helpful
I came on here because I have a very similar, if not identical problem.

I had the same idea (basically)

1 class called object, that has 4 purely virtual classes, living, physical, interactive and controllable.

the idea being that there are NPCs, which would inherit living, interactive and physical, the player would be living,physical and controllable. A health pack would be physical and interactive; A vehicle would be physical, interactive and controllable; etc.

All 4 virtual classes, that all more complex classes inherit the object class. The only reason it really exists is so that all the objects can be casted to a plain object and stored in the same array. Then when they are looped through, it could check each class to see if it was living or physical or interactive or controllable. If it was any of those, go through and run it's specific code.

Since the 4 base classes are virtual, every person type or player or vehicle would need to implement the inherited funcitions, so each living thing (for example) could check to see if it should interact with a health pack. A person interacts with a person differnetly that a person interacts with a health pack.

But, I'm starting to think that I should have an array of each of the base classes. So if a person is interactive, physical and living, a pointer of that class would be passed to each of the arrays. Since the pointer in each one would be referencing the origional class, it would be coherent.

There could be another object that assigns an id number to each object created, so when a person needs to be removed from the game/engine, it would go through each array, deleting any classes with the matching id number and the delete the origional class.

Coming on here and reading this thread and then writing out what I want to be able to do has helped me so much. Now to go see if I can make this work.

This topic is closed to new replies.

Advertisement