Question

I'm writing a game and it's going to use a component-based entity system. The first two components that I'm implementing are EntityRepresentation and EntityState. The representation holds the animations, and the state decides how the entity should react to game events (examples of EntityStates: standing, jumping, attacking, dead, falling).

The EntityRepresnetation decides what animation to draw on the screen depending on the Entity's EntityState. If the Entity is in "jumping" state, the corresponding animation will be played. (See the EntityRepresentation::changeAnimation() function.)

Here's roughly how I wrote the class...

class EntityRepresentation
{
  public:
    void draw(float x, float y, long currentTime, DrawingContext& dc) {
        // ...
    }

    void changeAnimation(const EntityState::Id& stateId) {
        currentAnimation = animationMap_[stateId];
    }

  private:
    map<EntityState::Id, const Animation*> animationMap_;
    const Animation* currentAnimation_;
};

There's something that I really don't like with my current approach... >:( The EntityState::Id part. Currently, the EntityRepresentation maps each animation it holds to a specific EntityState::Id. The ID is unique to every class that derives from EntityState (unique to the class, not the instance). These IDs are basically strings that have to be written by hand in the class' constructor, which means they are highly prone to name-collisions — more so since I plan to make the game scriptable (Python).

Can someone give me some advice as to how I can alleviate this problem I have with the IDs. I've seen a lot of implementations of the component-based system in games that also use IDs, but I still don't like them. It just rubs me the wrong way.

Maybe you can suggest a design change, such that the EntityRepresentation doesn't have to know the type of the EntityState, all while still preserving encapsulation.

Was it helpful?

Solution

Use typeid(), that's what it's for. You could use a const type_info* as the key type.

Alternatively, you know, you could just use actual inheritance, like, calling a virtual function to get the current animation, which would be much smarter.

OTHER TIPS

Assuming that the IDs associated with each EntityState are not needed at compile time, one trick I've used is to use pointers as an ID. This works particularly well if the EntityState is a singleton, in which case you could simply use the address of the singleton instance as the ID. Otherwise statically allocating a small chunk of memory for each ID works fine.

class EntityState {
public:
    EntityState(void *id) : mId(id) { }
private:
    void *mId;
};

class EntityStateJump : public EntityState {
public:
    EntityStateJump() : EntityState(getId()) { }
private:
    void *getId() {
        static void *id = new int;
        return id;
    }
};

Edit: Minor change to avoid static initialization order issues.

I suggest to implement the design using State pattern

class EntityState
{
public:
    EntityState(const Animation* animation);
    static const EntityState* getStandingState();
    static const EntityState* getJumpingState();
    static const EntityState* getAttackingState();

    const Animation& getAnimation() const;

protected:
    const Animation* pAnymation_;
};

class EntityRepresentation
{
public:
    void draw(float x, float y, long currentTime, DrawingContext& dc) {
        // ...
        const Animation& animation(state_->getAnimation());
    }

    void changeState(const EntityState* newState) {
        state_ = newState;
    }

private:
    const EntityState* state_;
};

void EventHandler(EntityRepresentation& entity)
{
    entity.changeState(EntityState::getJumpingState());
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top