MVC question in reference to gamasutra article "The Guerrilla Guide to Game Code"

Started by
7 comments, last by vladimirsan 13 years, 2 months ago
I was reading about an MVC architecture from this article: http://www.gamasutra...code.php?page=2

There are a few things I'm unclear about. Lets use a simple pong game as an example. We have two entities (models) paddle and ball. A paddle has a controller which can get input from a keyboard, mouse, or whatever else. Based on the input the controller decides what the paddle should do. So for example, if I press the up arrow on my keyboard the controller will receive that and will set a direction variable to up. The model will then poll its controller for the direction and move accordingly. My question is how exactly is the paddle's controller supposed to handle collision with the ball or the other way around. How are the models or controllers supposed to interact with other models. Is the controller even supposed to know about its specific model? It doesn't seem like this is the case according from this diagram (from the article):

[media]http://www.gamasutra.com/features/20050414/rouwe_00_clip_image002.jpg[/media]
Advertisement
Generally the input is just a bunch of requests, the simulation owns everything, and the rendering code just displays a copy of the state the simulation at some point (same for the audio, and networking output).

In a game, all a controller in the MVC sense is really getting you is some abstraction from the input device. Maybe you record the input and play it back for debugging, or write some code to simulate input for some freaky BOT or AI, or switch from a keyboard to a gamepad while the game is playing.
http://www.gearboxsoftware.com/
Couple of tweaks to your description:
  • You don't have two models - you have one, which is the entire game as a whole.
  • Each entity within the model is free to communicate with other parts of the model as needed.
  • You have one controller, which applies user input to the game; movement of the AI and ball/physics should be controlled by the model internally.


In general MVC is overrated for games. 95% of the work goes into the model, and it'd take a pathologically bad design to build a view/controller setup that doesn't conform to MVC's principles. So really... just implement it in a way that makes sense. You'll notice that 99.99% of the time, if you are careful, your architecture will look suspiciously like MVC anyways, even without going to the effort to specifically design around it.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


Generally the input is just a bunch of requests, the simulation owns everything, and the rendering code just displays a copy of the state the simulation at some point (same for the audio, and networking output).

In a game, all a controller in the MVC sense is really getting you is some abstraction from the input device. Maybe you record the input and play it back for debugging, or write some code to simulate input for some freaky BOT or AI, or switch from a keyboard to a gamepad while the game is playing.


I have the controller taking in input from a device, which works great. The problem comes in when I try to implement an AI. Lets take my pong example again. Obviously the AI controller will need to know the paddle's and ball's location in order for it to make a decision where to move. My question is where does the controller get this information from? Is it the entity that passes along this information or the entity manager or something else?

thanks for the feedback.

I have the controller taking in input from a device, which works great. The problem comes in when I try to implement an AI.
So, by definition, the controller is not working great, since it's not doing the job it's supposed to - it can't control.

Lets take my pong example again. Obviously the AI controller will need to know the paddle's and ball's location in order for it to make a decision where to move.[/quote]Under canonical MVC, you would encode this into model and view and controller would remain dumb.

The feedback from possible invalid moves would then be modeled as event from model that controller and view would subscribe to. Since this loses the coupling, one would need to tack on a full request/response pair handling mechanism, since each request would need a confirmation. This introduces the need for transactions. Transactions can result in deadlocks, which means a transaction broker must be added along with a priority handling system. And since behavior in model would need to change, it would be defined using policies, strategies, rule engines and external scripted logic.

And at this point - you have reinvented the full-blown enterprise-grade application stack.

Everything is correctly decoupled, everything is nice and modular, but suddenly 99% of infrastructure is just plumbing - instead of having direct access to state and saying "if (!moveValid()) return;".

This is precisely why enterprise frameworks (.Net, JEE) become such beasts. They model everything and each requirement complicates by covering fully orthogonal functionality, resulting in dimensional explosion. But they cover everything.

Omitting any of the above exposes a potential authority or logical problem where some behavior may "fall through the cracks" and ends up being difficult to fix or looks like a hack, making it difficult to extend and maintain.



One problem with MVC is that it's encapsulation is inherently orthogonal with how the simulation works. MVC tacks everything into objects, treating them as independent entities. But in reality, the simulation updates entire state at once, over and over. It never updates objects independently, even if MVC tries to present it as such.

So the proper organization, rather than focusing on individual entities is to turn things around - design everything around state. State is the board:List<Entity> entities;
List<Mob> mobs;
List<...> ...;
Next, the controllers get access to the state - not individual entity.

Same for view - it's not a completely separate entity, it works in tandem with model and controller. Then on each step:update_controllers(state);
update_view(state, controller_results);
update_simulation(state, view_results);
render();

The "xxx_results" is a list or some other storage which collects the updates to transfer between systems. Controllers might report collisions or UI feedback (screen flash). View will then show up buff icons, change full screen alpha to red and similar.

How exactly this ends up being structured is not all that relevant, the key is to treat state (aka model) as a whole to which each other system has access as a whole. There simply isn't any benefit in trying to pretend that A is forbidden from knowing about B. Low coupling is achieved differently, mostly by not going crazy in inheritance and abstractions.

[quote name='subflood' timestamp='1297091210' post='4770892']
I have the controller taking in input from a device, which works great. The problem comes in when I try to implement an AI.
So, by definition, the controller is not working great, since it's not doing the job it's supposed to - it can't control.

Lets take my pong example again. Obviously the AI controller will need to know the paddle's and ball's location in order for it to make a decision where to move.[/quote]Under canonical MVC, you would encode this into model and view and controller would remain dumb.

The feedback from possible invalid moves would then be modeled as event from model that controller and view would subscribe to. Since this loses the coupling, one would need to tack on a full request/response pair handling mechanism, since each request would need a confirmation. This introduces the need for transactions. Transactions can result in deadlocks, which means a transaction broker must be added along with a priority handling system. And since behavior in model would need to change, it would be defined using policies, strategies, rule engines and external scripted logic.

And at this point - you have reinvented the full-blown enterprise-grade application stack.

Everything is correctly decoupled, everything is nice and modular, but suddenly 99% of infrastructure is just plumbing - instead of having direct access to state and saying "if (!moveValid()) return;".

This is precisely why enterprise frameworks (.Net, JEE) become such beasts. They model everything and each requirement complicates by covering fully orthogonal functionality, resulting in dimensional explosion. But they cover everything.

Omitting any of the above exposes a potential authority or logical problem where some behavior may "fall through the cracks" and ends up being difficult to fix or looks like a hack, making it difficult to extend and maintain.



One problem with MVC is that it's encapsulation is inherently orthogonal with how the simulation works. MVC tacks everything into objects, treating them as independent entities. But in reality, the simulation updates entire state at once, over and over. It never updates objects independently, even if MVC tries to present it as such.

So the proper organization, rather than focusing on individual entities is to turn things around - design everything around state. State is the board:List<Entity> entities;
List<Mob> mobs;
List<...> ...;
Next, the controllers get access to the state - not individual entity.

Same for view - it's not a completely separate entity, it works in tandem with model and controller. Then on each step:update_controllers(state);
update_view(state, controller_results);
update_simulation(state, view_results);
render();

The "xxx_results" is a list or some other storage which collects the updates to transfer between systems. Controllers might report collisions or UI feedback (screen flash). View will then show up buff icons, change full screen alpha to red and similar.

How exactly this ends up being structured is not all that relevant, the key is to treat state (aka model) as a whole to which each other system has access as a whole. There simply isn't any benefit in trying to pretend that A is forbidden from knowing about B. Low coupling is achieved differently, mostly by not going crazy in inheritance and abstractions.
[/quote]

If the controllers know about the entire state how do they know which objects to communicate with? Is it just: for(Gun : state.gunList) do this

Under canonical MVC, you would encode this into model and view and controller would remain dumb.


Could you elaborate on this...I understand the reason why this should be in the model...but for example If I want to take control of the player's avatar (let's say for a cut scene) I thought that it was only a matter of changing the avatar controller (assuming that the controller is an interface or something like that). Am I wrong? How can I then implement that?

unsure.gif


Check out my blog: vladimirsan.com

Could you elaborate on this...I understand the reason why this should be in the model...but for example If I want to take control of the player's avatar (let's say for a cut scene) I thought that it was only a matter of changing the avatar controller (assuming that the controller is an interface or something like that). Am I wrong? How can I then implement that?


Player controller:
Player moves forward. Player hits wall. Physics responds and generates collision, particle system generates debris, screen shakes, health is decreased.

A lot has happened...

What about cinematic?
(move_forward, move_forward, move_forward, move_forward, jump, say "hello"). After second move, due to some inaccuracy or poor planning, the player avatar bumps at a rock. Script obviously doesn't handle that, so it keeps moving forward despite being stuck. Debris starts flying around, player loses health, player dies. Player corpse says "hello". Definitely not what was intended.


This means you need to encode different movement rules into model. One control is for interactive movement, the other is for scripted movement. Controller needs to be dumbed down to not respond or know about actions as such. It cannot know about physics, particle systems, UI, ... Model then becomes something like this:class Model {
move_direct(Object o, float x, float y) {
if (!ignore_input) {
check_for_collision(x,y);
o.update_position(x,y);
}
}
move_scripted(Path p) {
set_ignore_physics(true);
set_ignore_collision(true);
set_ignore_input(true);
o.set_path(p); // regular scheduler will be moving along path
o.set_path_end_listener( end_of_path_reached);
}
end_of_path_reached() {
set_ignore_physics(false);
set_ignore_collisions(false);
set_ignore_input(false);
}
};

Controller is not dumb, it can only request an action to be performed. The results of these actions will be conveyed via listeners.

Design remains clean and completely de-coupled, but without considerable complexity in such asynchronous logic one can break the model completely. What if script puts player into lava? What if script moves through locked door?

In order to keep subsystems separate, all such conditions must now be checked separately, once for human control and once for scripts. Alternatively, there could be some feedback handler validating that script is executing correctly by checking the physics and allowing some control when it doesn't.


Core problem is that scripts themselves are dumb. They don't know how to react so the way they are represented must either be flexible enough to recover from errors (use move targets instead of fixed positions, give timeout for all actions to prevent getting stuck in a loop, ...) or it must turn off parts of model invariants, potentially risking breaking the simulation. Former is probably preferred, since even though animation would break, model would remain consistent.
Hey thanks a lot! I really have a better understanding about it now laugh.gifohmy.gif
Check out my blog: vladimirsan.com

This topic is closed to new replies.

Advertisement