Sponsored By

Applying the Model-View-Controller Pattern

Model View Controller (MVC) Pattern is an extremely useful OO modeling technique. In this post I reflect about why it is useful and how we applied it in our projects.

Ivica Aracic, Blogger

September 19, 2010

4 Min Read

Introduction

During my research and teaching work at the university I often had to review and understand large code written by other people. Every time I had to do the task, I started with isolating the model of the application, which captures the essence of the software (a.k.a. "domain logic"/"business logic"). Ideally people applied the MVC pattern and it was easy to find it, but in most cases it was tangled with other technical layers, most commonly with the UI/Rendering. Unnecessary to say that this tangling makes the understanding and lot of other things much more difficult.

Also when reading books on game development or reading articles in various game development magazines, I often see software architectures presented, having no clear separation between the model and the view.

For instance, as recently observed in an article in a german game development magazine (<- : inheritance arrow):

GameObject <- GameEntity <- AnimatedGameEntity <- Character
                   <- ParticleSystem

 Most people will ask now: what's wrong about this anyway?

Well, the biggest problem here is the tangling of the technical layers. The tangling between the domain logic (GameObject, GameEntity, Character) and the view (AnimatedGameEntity, ParticleSystem).

This is exactly the problem addressed by the MVC-Pattern. This pattern isolates "domain logic" from input and presentation, permitting independent development, testing and maintenance of each. In the following dependency graph, we can see that the model is at the bottom having no further dependencies.

C O N T R O L L E R
   |    |
   v    |
V I E W |
   |    |
   v    v
  M O D E L 

Moreover, there are many more benefits:

  • better focus on the essence of the problem: when view and model are tangled, it is often difficult to focus on the domain logic. This often leads to unnecessary complexity and further tangling.

  • easier model reuse in a different context: the domain logic is the most expensive and the most valuable part of the software. when you port your software to a different platform for instance, the "domain logic" remains, but view and controller will most probably change. 

  • reduction of dependencies: everything in the program will somehow depend on the model, since it captures the essence of the software, hence, it is a good architectural decision to keep the model narrow as possible and avoid that it depends on other unnecessary things.

How do we apply it?

MVC is not easy to code in OO. The reason lies in the problem, that a game object is crosscutting through the technical layers (model, view, sound, ki, input, ...). This means, if we separate a game object class according to the technical layers, we get n classes (for each layer one), but at the end we wish to have one single object.

For instance, in our game there is a weight which can be thrown into the level. Divided according to the layers, we get:

domain logic (model): position, gravity, collision, behavior (state machine), ...

view: its graphical representation, which includes the weight model and all of its animations

so far covered by MVC, but there is more, for instance:

sound: when the weight starts, a sfx is triggered which runs till the weight reaches the floor, then another sfx for the contact with the floor is triggered.

ki: when defending the weight, ki has to keep track on some additional data about it. 

We model this in our projects by making the GameObject extensible via the adapter pattern.

class GameObject {
public:
    virtual void registerAdapter(TypeInfo const& typeInfo, void* adapter) = 0;
    virtual void* getAdapter(TypeInfo const& typeInfo) = 0;
};

Now, when for instance the view gets the event, that a new game object is created, it creates automatically a ViewAdapter for it and registers it for the corresponding game object. Moreover, view adapters can be synchronized with the model each frame or when specific model events occur. In the following, a schematic example for the weight:

class Weight : public GameObject {...};

template <class T> class ViewAdapter {
public:
    ...
    void syncWithModel(float dTime) = 0;

protected:
    T* modelRef;
};

class ViewWeightAdapter : public ViewAdapter {
    ...
    void syncWithModel(float dTime) {
        node->setTranslation(modelRef->getPosition());
        ...
    }
    ...
    void onGameObjectModeChangeEvent(GameObjectModeChangeEvent* e) {
        if(e->go == modelRef && e->toState == WeightModel::FLOOR_CONTACT) {
            anim->triggerAnim(ANIM_FLOOR_CONTACT);
        }
    }
    ...
    Mesh* weightMesh;
    SceneNode* node;
    AnimationSet* anim;
};

ViewEngine::onGameObjectCreateEvent(GameObject* obj) {
    if(obj->getTypeInfo() == Weight::typeInfo) {
        obj->registerAdapter(
            ViewAdapter::typeInfo,
            new ViewWeightAdapter((WeightAdapter*)obj));
    }
}

ViewEngine::syncAllWithModel(float dTime) {
    ... for each adapter call syncwithModel(dTime) ...

The same applies for other layers too.

Read more about:

Blogs

About the Author(s)

Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like