Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C++, should different game entities have different classes? Or should it be in one class which contains all behaviours?

I am working on a game environment which will feature a number of different entities. Each of this will have a few common functions (Draw, Update, etc) but sometimes the game must treat them differently based on their enemy type. So far, I have encoded the "type" of enemy in their instances' class. So, we have a situation like:

class MotionObject { ... };

class Entity : public MotionObject { ... };

class Coin : public Entity { ... };

class TextSign : public Entity { ... };

class ShapeEnemy : public Entity { ... };

class Attractor : public ShapeEnemy { ... };

class Bumper : public ShapeEnemy { ... };

So the classes Coin, TextSign, Attractor and Bumper are the types of entities instantiated in the game. Having different classes for different types of entities feels right, but I can't shake the feeling that some cumbersomeness might be avoided if I just had one entity class, which contained a switch statement that controlled its behaviour based on its "entity type", something stored in a variable. The player interacts with these entities in different ways depending on their type, and I use dynamic_cast and a null test each time to figure out which behaviour is applied. To be clear, these are for behaviours where I can't call a simple Update() on each entity; the player will respond in a specific way, or their will be inter-entity interaction, all based on the entities type. My code looks like this:

void Game::Update(float deltaT) {

    for (int i =0; i < DrawChildren.size(); i++) {
        //each entity has its Update called
        DrawChildren[i].Update(deltaT);

        //What follows is code that the Game class needs to run based on the entity type.
        Coin * coin = dynamic_cast<Coin*>(DrawChildren[i]);
        if (coin != nullptr) {
            ...
            continue; //so no other type's code executes, in case I have inherited types.
        }

        TextSign * textSign = dynamic_cast<TextSign*>(DrawChildren[i]);
        if (textSign != nullptr) {
            ...
            continue; //so no other type's code executes, in case I have inherited types.
        }

        Attractor * attractor = dynamic_cast<Attractor*>(DrawChildren[i]);
        if (attractor != nullptr) {
            ...
            continue; //so no other type's code executes, in case I have inherited types.
        }

        Bumper * bumper = dynamic_cast<Bumper*>(DrawChildren[i]);
        if (bumper != nullptr) {
            ...
            continue; //so no other type's code executes, in case I have inherited types.
        }

    }

    ...

}

Is there a less cumbersome way to do this?

like image 300
escapecharacter Avatar asked Oct 21 '22 00:10

escapecharacter


1 Answers

One alternative approach is to have a "shallow" class hierarchy and independent "behaviour" abstract classes that you can use to decorate your main hierarchy.

So you can have:

Entity

-> Character -> NPC ->....

-> Object -> Container -> .... Renderable

Moveable

Killable

Equippable...

You'll use multiple inheritance to decorate the classes.

Having a single hierarchy forces you to move features up the tree when they are shared between different "subtrees", which means you add features to classes that might not use them, complicating the whole design; or otherwise, you end up having to repeat code (DRY principle?). In both cases, this becomes harder to maintain and expand.

like image 198
Calimar41 Avatar answered Oct 23 '22 21:10

Calimar41