3
\$\begingroup\$

I need some advice on how to design the Entity module in my game, how to apply the MVC pattern and generally how the Entity should interact with its controller and its representation.

First some details about the game: it's a 2D action-platformer, it's data-driven (or rather, I'd like it to be data-driven — it isn't yet) and the Entities should be scriptable (I'll be using Python).

I'm still at the very beginning of the development and I have some general ideas about how the Entity module should work, but I get stuck on the details. Here's roughly what I have up until now:

class Entity
{
  private:
    // The representation holds the actual animation and sounds that
    // are used to render the entity. The representation can read the
    // Entity's state but it cannot alter it.
    EntityRepresentation* representation_;

    // The controller handles events and changes the Entity's state
    // based on these events.
    EntityController* controller_;

    // Here's some of Entity's attributes
    struct Vector{ float x, y; } position_;
    float directionRadians_;
    float speed_;

  public:
    void handleEvent(const Event& e) {
        controller_->handleEvent(e, this); // It's obvious what it does :)

        // The representation needs to handle events so it can change the
        // current animation, queue up a sound for playing, etc. In my
        // current setup it doesn't yet need to take 'this' as a parameter.
        representation_->handleEvent(e);
    }

    void update(unsigned long timePassed) {
        position_.x += speed_ * timePassed * std::cos(directionRadians_);
        position_.y += speed_ * timePassed * std::sin(directionRadians_);
        // .. And any other updates I might need to do... 
    }

    void render(DrawingContext* context, unsigned long timePassed) {
        representation_->render(context, timePassed, position_.x, position_.y);
    }
};

There are a few things I think could be problematic in this setup. For one, if you look at the Entity::handleEvent() function you will see that the Entity forwards the event to the controller as well as to the representation. The controller uses the event to alter the state of the Entity; the representation however uses it to alter its own state (to use some other animation, to decide whether it should play a sound, etc.).

This means the representation has a state of its own which is characteristic to this Entity only. Do you think it's bad that each representation can only be used for one Entity? (Note: this does not mean that in case there are two entities using the same sprite, that sprite is loaded twice in memory: the sprite itself is a common resource.)

Another thing I'm having problems with the deciding how to organize the actual entity data on disk. Where should the bounding box info be read from: the animation or somewhere separate? should there be a single animation file for each character? should scripts be separate from the data or should everything related to one type of entity be stored in a single folder (scripts and animation and sounds)?


tl;dr Can you give some some general advice on how the Entity should interact with its controller and its representation (i.e. sprites and sounds). Also, do you have any advice on how the data itself should be organized on the hard-drive?

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

(My tl;dr -> Just make a friggin' game!)

For the MVC part of your question, have you read through this question?

I think keeping a 1-to-1 relationship between Entity and EntityRepresentation is the right choice. The things you'll want to share across multiple entities can be referred to in the Representation or Controller (like the Sprite loaded in memory as you discussed). This way, you are able to make per-instance changes to the Entity.

Say you added the option in EntityRepresentation to apply special effects, like a colored glow. If you shared one EntityRepresentation instance among multiple Entity instances, you wouldn't be able to choose one specific Entity instance to add the glow effect to.

Have you written your EntityRepresentation class? Something to think about is cases where you want image and sound vs image but no sound vs sound but no image.

My train of thought regarding that is:

  • Okay, have different subclasses of EntityRepresentation to represent each.
  • Except that's wasteful performance-wise, just give EntityRepresentation two pointers to an Audio and Visual representation respectvely Easier/faster to check NULL than to have another virtual call and making your inheritance chain deeper.
  • But then that leads me to the conclusion, why even bother having EntityRepresentation? just have an AudioRepresentation and VisualRepresentation in the Entity class and do the null checking in Entity. It's just one additional handle_event call.

As for file organization, I don't think it makes a significant difference so long as you keep it organized. I personally prefer to keep (for example) images and scripts in separate folders even if they are associated with the same type of entity. I do keep a similar subdirectory hierarchy in both the images and scripts folder though.

ie:

  • images/Enemies/Vogon/Vogon_idle.png and images/Enemies/Vogon/Vogon_walk.png
  • scripts/Enemies/Vogon_config.py and scripts/Enemies/Vogon_AI.py

There is a miniscule workflow benefit for having all Vogon related files in one folder in that you don't have to keep multiple file browser windows open. But this is a really trivial difference.

A lot of this stuff is really trivial. Just keep making the game and go with your gut instinct on organization and the Entity class structure. I like your current layout of the Entity class. Work with it and then make some decisions. You'll be better informed about your style of programming and about the game you want to make after you actually start trying to make this game. Starting with separating the representation as you are now will give you enough wiggle room to change things up later down the line.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .