I was reading through the tutorial requests and took note of an old post by Kadoba:
How about a tutorial over the MVC (Model View Controller) architecture pattern for games? I've found different reading materials over it but they're all very vague and inconsistent - especially the placement of the subsystems. Like, one article I've read says to implement input in the view and another says it should be implemented in the controller.
So basically a MVC take on how to organize your subsystems, like the ones below, into a cohesive whole:
Resource Management
Entities
Collision Detection
OS Specific Stuff (window handling, OS events)
Events/Messaging (game specific, not OS)
UI
Game Loop
Input
Output/Rendering
Sure thing, thinks I. I'll help out a bit anyhow and clear up some concepts for you.
To start with, I'll clear up some of the confusion for you.
MVC is a high-level composite pattern. It's built on a tried and tested system and is very flexible.
The closest thing to an analogy of how this system works is your own computer.
The Model, in this case, is your hard drive, your RAM, your cache, your graphics card RAM. Anything which is capable of holding a state, whether it is static as on a hard drive or dynamic as in your RAM and cache.
The View would then be any interface you have with your computer. Including, but not limited to, your display, keyboard, mouse, USB ports, mic or web camera. All of these are stateless objects which process the content that they receive and output them to another medium.
Finally, the analogy for the Controller is whatever software is running on your computer and interpreting the input and the state of your model.
This is also why most tutorials seem a bit confusing on the point of input. All input is delivered to a view and passed to a controller in a form the controller understands.
An easy example here would be a network connection. The input view in this case is a network receiver running in it's own thread and listening for data from a socket. When the view receives data, it's translated into an internal class and sent to a controller for processing.
Similarly, keyboard input would be a view running in a separate thread which generates keyboard events when these are collected from the operating system, again passing these to a controller.
The easier way, at least in gaming projects that aren't solely based on the mouse and a set of buttons, may be to separate the view into input systems, output systems and combined systems.
Finally, most tutorials and guides to MVC also focus on three different classes, a View, a Model and a Controller. This is a gross simplification, applicable in only the simplest of cases.
---------------------
With all of this out of the water, we can start exploring how MVC works in a game environment.
The first thing to note is that we need a lot more granularity.
Let us sketch out the basis of a game by specifying some basic details we want in it.
Let's say we're working with a system where we want to be able to
save and load between
levels, we have a set of
entities that are
loaded from the hard drive
on start, all
scripted in lua. We're using
opengl, the
keyboard, have a
networked scoring system, a
time limit and we're doing it all with
sprites in
2D. Furthermore, it is a
real-time game where we use some kind of
bullets and have a
constant vertical motion on the
player.
Let's cut the crap, we're making Tyrian-only-better with OpenGL acceleration, saving/loading and a scoreboard. (Although, for the purpose of this tutorial, I'll be leaving out some things in Tyrian from my description.)
So, right, we'll also be needing a
menu system, meaning
several modes of execution and a way to
change mode. Also a
shop,
money,
powerups,
weapons,
enemies,
pathing,
collision detection and finally,
sound.
Remember, most of this is things I won't be implementing per-se, at least not in this tutorial. My goal right now is to give you a concept of how MVC-guided design may work.
Our goal now is to separate things into what they'll optimally be implemented as. While this is, for all purposes and intents, a matter of opinion, I'll present a suggestion as to how I might go about it, while trying to motivate the structual benefits.
Now, again, the layout of MVC is simple and flexible. I'll try to discuss it in more detail before I separate things up.
First, the Model. It is generally (always) implemented as a set of data, where changes are
observable. This is important, the observer pattern is a famous and simple pattern which is used to great effect in MVC.
The basics are just that, basic. Instead of constantly checking if a value has changed, we subscribe to changes on a value.
A very simple implementation would look like this in my made-up language:
class observable{
addObserver(observer obs){observers.add(obs);}
setValue(v){value = v; for_each(o in observers) o.valueChanged();}
}
class observer{
valueChanged();
}
In the most common situation described in tutorials, the views subscribe to changes in the model and update their view when a value changes. In some cases, it may be that the view shouldn't be updated until a specific call is made, updating every view. If so, it is generally stored that the view has been invalidated and should update on the next update call.
Ideally, the model or models contain all state data and nothing else. If this is fulfilled, the model can be serialized and restored at any point with new views and controllers, still in a valid state. Complete decoupling of state from the program... Essentially saving and loading for free!
The view was described in detail earlier, it is your interface to other systems, be it the hard drive, mouse or display. A very, VERY common implementation method is a
composite pattern. A high level object contains sub-objects like buttons and panels which contain text elements and so on. In a game environment this may be desirable in some cases and not in others. Generally speaking, an output may do better as a composite, while an input is probably a single object and running in a separate
thread. A rule of thumb is that any input will probably do better as a separate thread.
Note the implications this has for the rest of the project. You will want to have your state, the model, mutually exclusive to be certain that you stay in a valid state. (synchronized in Java terms, if you prefer.)
Again, an example:
class output_view{
add_child(output_view oview){children.add(oview);}
draw(){for_each(c in children) c.draw();}
}
class input_view : thread{
main(){
while(running){
input = wait_for_input();
translated_input = translate(input);
controller.handle_function(translated_input);
}
}
}
Finally, the controller. The controller is another stateless object which handles all the algorithmically intensive things in MVC. If we view MVC as a state machine, the controller controls how we switch between states. Actually, most often the controller is implemented as an interchangeable object, or a state machine. This is known as the Strategy pattern. Even easier than observer, strategy is based on the concept of an interface of functions where the implementation is switchable.
Example:
current_controller = startup();
current_controller.menu();
current_controller.shoot();
current_controller.menu();
class startup : controller{
shoot(){current_controller = in_game();}
menu(){/*do_nothing*/}
}
class in_game : controller{
shoot(){shoot_bullets();}
menu(){current_controller = menu();}
}
class menu : controller {...}
Consider how we could combine this with an input view. The keyboard being the best example.
A "Z" is sent to the input view. This is translated to "Run the shoot() function in the current controller."
In our startup state, this may be related to "Press Z to continue..." which is so common on intro screens nowadays, moving us to the menu state or game. While playing however, pressing Z will fire a bullet. Only the controller needs to be changed to cause this behaviour modification. Essentially, the behaviour is decoupled from the data model, input and output.
In most games it is probably better to move the controller content into scripts, resulting in an easier-to-manage game. What we keep in the controller is what we need to have natively, like algorithms that need better performance than scripts can offer, as well as other native systems that the scripts call via an interface, like exit() to end the game or save() to save.
Now, with a grip on how MVC layouts work, let us separate everything up into logical components:
Model | levels, scripts, entity-states, time, money, current-mode |
Input-View | load, start-load, lua-interface, scoreboard-connection, keyboard, timer, repaint |
Output-View | save, lua-interface, opengl, scoreboard-connection, sprites, sound |
Scripts | bullets, player, powerups, weapons, enemies, pathings, menu, shop |
Controller | mode-change, collision-detection |
While the controller looks slightly empty, this is only because most of the details described are heavily related to the models and views, while the controller acts to bind them together. For example, while the timer is an input system, the controller does whatever is needed when the timer ticks (in our case, running all scripts if we're in the game state and nothing if we're in the menu) but the interesting bit in the context of MVC is the actual timer, not what it will result in, and so it is in a view. Scripting also takes a heavy load off our Controller, as most of the script content would be in the controller otherwise.
As for motivations, most of these should be quite clear, but I'll try to explain.
levels, scripts, entity-states, time, money, current-mode
Levels and scripts are generally blobs of data that decide how other things should work later on. Storing them someplace in-memory is useful even if they do not need to be saved. They would thus be in a model section which was not stored permanently.
Entity state, or, in this case, positions, descriptions, current sprites, pathing and the like, would be stored in a model and thus potentially saved on shutdown. Most interesting for saving would probably be the player state, including difficulty, score, money, level, et cetera; but again, we are separating the state and placing it in a common location for easy retrieval. The observer pattern also makes it easy for us to see when an entity has changed, allowing for example collision detection to run whenever an object moves.
The current mode is interesting to store in the model, even if it's an abstract part of the controller, as this potentially allows us to save mid-game (meaning we'd have to save all entity states, yes) without any changes whatsoever to the rest of the code. We'd still just serialize the model and unpack it to continue.
Time is saved in the model for much the same reason as the other content, by this, we avoid placing a state in the timer, instead just having it tick at a constant rate. This again would make it easier to save and load without having to handle some things specifically.
load, start-load, lua-interface, scoreboard-connection, keyboard, timer, repaint
All these are inputs from an external system. Repaint could be done via the timer in the controller, placing it in a separate thread allows for timing management in case of performance issues in some circumstances, but it might be more relevant in a physics simulation. The Lua interface and the scoreboard connection are also oddballs as they are complete IO-views. Implementation-wise, this should not create any humongous problems for most developers.
Loading and start-loading are simple data retrievals into the model, deserialization and script loading are, in the whole, details that are best left to an input system, even allowing for decoupling into separate threads where a startup loader could notify when the startup script is ready for execution and then continue loading sprites and content in the background. (Again, this is probably a lot more relevant in a 3D game with a huge amount of textures and 3D-models.)
Other details which may pop up as input views are resize requests, shutdown requests and other windows messages. These may be passed to a controller and handled as appropriate. (Change the orthographic projection for example)
save, lua-interface, opengl, scoreboard-connection, sprites, menu-system, sound
Saving is probably the easiest one here is if implemented correctly, as it would be a simple matter of serializing the model into a file when a script or controller requests this action. The lua interface on the output end is mostly execution of scripts and in some cases execution of special script events like callbacks and such which would be run through a controller.
OpenGL is where composition may come into play by adding sprites into the drawing surface which are recursively drawn with on-screen constraints based on their parents. This may be done in several layers, where the OpenGL textures could update when an observable entity state changes and sprites could be sent as vectors into a controller and passed to the OpenGL view which would draw them in a single batch call. The scoreboard connection on this end would be sending scores to a server, while the menu-system would be a composite object also working with OpenGL to draw text and such in a menu group in a specific style, taking load off the scripts. Of course, the menu could also be done completely in a script.
It is noteworthy to mention that composite as a pattern is more effective the more complex a view gets. If you're working in a 3D environment, the world is pretty much always drawn using a scene-graph. A scene graph is implemented as a tree structure where nodes contain translations, scaling, rotations, textures and effects which are applied to child objects, while leaf-nodes contain models and 3D geometry. This maps 1:1 to how the composite pattern is structured and generally works in the same way.
bullets, player, powerups, weapons, enemies, pathings, menu, shop
All of these are quite clearly optimal scripting material. Actually, they all work
exactly like a set of controllers. They are a set of functionality with a standardized interface that may be replaced at will to produce a different result, which is also why the controller is so empty, which is nice as working with a script is generally a lot more modular than working with hard coded systems.
mode-change, collision-detection
These are also fairly easy. Mode changes like entering a level or starting the game are partially based in scripts, but the actual behaviour change is situated in a controller which replaces itself. Collision Detection is possible to implement as a reactive system where collisions are sent as messages to relevant scripts whenever a collision box changes utilizing an observer pattern or just as a simple passive algorithm that is called via a lua interface. Both solutions are viable, although a passive approach may be slightly easier to take advantage of in scripting.
For now, this should be sufficient as an introduction to how MVC could work in a game environment. While mostly theoretical, I hope you found this interesting.
If there is any interest, I may continue and describe some implementation details later on.
Also, I must reiterate, this is purely an example of how MVC
could be applied to game development. There is no strict laws defining how MVC and OOP patterns work, only suggestions and rules of thumb that may or may not happen to be useful in your situation and may or may not adhere to how you like to program. If there is a solution you would prefer over this regarding MVC or even programming in general, go for it, your solution is probably better for you!
Linus