Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411273 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 01:30:47 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityTownhallForum IssuesArchived subforums (read only)TutorialsMVC in game development
Pages: [1]
Print
Author Topic: MVC in game development  (Read 13866 times)
Linus
Level 0
***


View Profile
« on: February 10, 2010, 04:37:16 PM »

I was reading through the tutorial requests and took note of an old post by Kadoba:

Quote
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. Smiley

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:
Code:
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:
Code:
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:
Code:
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:

Modellevels, scripts, entity-states, time, money, current-mode
Input-Viewload, start-load, lua-interface, scoreboard-connection, keyboard, timer, repaint
Output-Viewsave, lua-interface, opengl, scoreboard-connection, sprites, sound
Scriptsbullets, player, powerups, weapons, enemies, pathings, menu, shop
Controllermode-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
« Last Edit: February 10, 2010, 04:43:38 PM by Linus » Logged
jotapeh
Level 10
*****


View Profile
« Reply #1 on: February 12, 2010, 04:19:29 PM »

So far this is pretty interesting, just thought I'd say that.  Hand Thumbs Up Left
Logged
george
Level 7
**



View Profile
« Reply #2 on: February 12, 2010, 05:44:07 PM »

Agreed, excellent write-up Linus.
Logged
Falmil
Level 6
*


View Profile
« Reply #3 on: February 16, 2010, 12:57:30 PM »

Most of this I'm already familiar with, though it helps to see it all at the same time. One of the parts I get hung up on is trying to neatly separate the general library/engine code from the game specific code/scripts. It would be nice to just tell the Model to make an game object for me by passing it the object type as a string, but that would require it to know all the game objects I'm using for it to pick out the right one, which is probably something it should not have knowledge of.

Here are a couple pages on MVC in games that I look at from time to time to get ideas on organizing things.
http://www.mine-control.com/zack/patterns/gamepatterns.html
http://www.gamasutra.com/features/20050414/rouwe_01.shtml
Logged
Linus
Level 0
***


View Profile
« Reply #4 on: February 16, 2010, 04:54:24 PM »

When you say "game object", I expect that you mean a scripted entity of some sort. I can see a couple of ways to make this happen.

By far the easiest way to implement would be to create a file containing a list of scripts in your game. Upon loading you would read this file and load all the scripts into a model of some kind.
A metaphor for this would be a sprite sheet which is read and cross-referenced to whatever name and position you want a sprite to have.


More flexible would be to on game start traverse all scripts in a specific directory. You would load and parse all scripts in turn and run a pre-defined function in each script. An example, in pseudocode:
Code:
//Script
function _register(){
     //Register this script with the desired name.
     entity_register("script_name");
     set_global_flag("myflag", "content");
     //Generate or load non-instance specific data, such as sprites, 3D models, animations, et cetera.
}
Code:
//IViews
class IView_ScriptLoader{
    //Called around startup
    iview_scripts_load(){
        foreach(file in scriptdir)
            scriptLoaderController.script_new(script(file));
    }
}
class IView_Scripts{
    //Called via script system
    iview_script_entity_register(instance, name){
        scriptController.script_entity_register(instance.script, name);
    }
}
Code:
//Oview
class OView_Lua{
    oview_lua_configure(script){
        //configure a script with a Lua engine.
        script.setSubsystem(LuaSubSystem());
    }
}
class OView_Scripts{
    OView_Scripts(){
         //Whenever a script is compiled we want a hook to register the script once compiled.
         //This way of hooking callbacks isn't necessarily possible in your language of choice,
         //you may need an interface or something comparable for the observer.
         script::observe_subsystems_post(oview_script__register);
    }
    oview_script__register(script){
        //Create a temporary instance and call a registration function with it.
        script.getSubSystem().exec(instance_temp(script), "_register");
    }
}
Code:
//Controller
class Controller_ScriptLoader{
    script_new(script){
        //configure any new script with a lua subsystem.
        lua.oview_lua_configure(script);
    }
}
class Controller_Scripts{
    script_entity_register(script, name){
        //add the script into a data model where it can be found later.
        scriptsModel.add_script(script, name);
    }
}

With this kind of setup the scripts would register themselves on creation. Notice also how we with relative ease even could replace the scripting language. Perhaps a bit over-engineered, but still interesting. Cool

Edit: A hybrid, but perhaps even more extreme solution would be to have a single script that was always loaded, allowing this script to load content into the game.

Placing a registration function in the script itself should to a great extent help move the game logic from your hard coded program to your scripts. In theory, you could have a "script()"-function in your script language which took a string and generated a new script that could then be referred to by other scripts after registering itself, so you could even create new scripted entities inside your scripts.

Would this solution be adequate for your needs? Gentleman


Also of note: I'll probably be continuing the tutorial at some point in a close future, since this is a fun project.

Fun related fact: In relation to the second tutorial, I wanted to try utilizing UML to generate the class layout and some basic structures for free code. UML managed once again to remind me of how horrible it is. Perl and a make-file managed to do the same thing in less than the time it took me to get UML working. Minus the images, but they can always be generated afterwards; as we've all done in class.


Linus
« Last Edit: February 17, 2010, 01:31:01 PM by Linus » Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic