occasionally useful ruby, ubuntu, etc

12Dec/100

MVC, Games, and a message bus

It seems to me that many approach the use of MVC in games with skepticism, but the more I develop a game in MVC, the more I am reassured in my decision to do so. I operate by the principle that models should absolutely not know about their view(s) (or controllers) -- models should be completely standalone chunks of state and logic that can only reference other models. However, this can present us with a few problems.

Assumptions

The way I design my games, generally I have a World model (which stores pseudo-global state, and with which every other Model is associated) that broadcasts a tick() method to every model in the known world and subsequently a paint() method to every view in the viewport at regular intervals. The Views know about their respective Model.

Interacting with Views

One of the problems I've faced is sometimes a model simply has to actively interact with its view. For example, suppose a gem-encrusted sword laying on the ground is put into a character's backpack. You want the view to stop painting every 50ms (that is, stopping calling its paint() method), for performance reasons. The view can see that the sword model is packed on the next paint() cycle, but if it stops paint()ing after that, how do you get it to start again when the sword is unpacked? You can't check inside the paint() method; it's not executing anymore. You have two options here: completely destroying and recreating the view later, or putting the view to "sleep" and waking it later.

Destroying+Recreating the View

The view checks

@my_model.isInBackpack

and if true, it cuts itself out of the render loop. Problem is, it does this in the paint() loop (since that's the only method called on views regularly), and this means it'll never paint again! Good for performance, bad for "okay how do we get it to start paint()ing again". Unless every model only has one possible view configuration (which...would defeat the point of separating them), nothing keeps track of exactly what the view's parameters were before it was destroyed. For example, the GemEncrustedSwordView might have three different images to represent the sword, decided at random at view instantiation.

The model certainly can't recreate this -- it knows nothing about views. We could create some sort of ViewDatabase object that maintains a map of Model->ViewParameters, so something knows about what the view parameters should be, but that's messy and we still don't know what entity will be responsible for actually creating the view (or knowing when to do it). And what if there are multiple live views for the same model (say a minimap)?

Putting the View to Sleep and Waking it Later

Instead of completely destroying the view, you can simply put it to sleep. This solves the problem of maintaining the view's state at the expense of memory usage. Still there's the problem, how do you wake it? Do you compromise the rules "just this once" and let sword models reference their views, so that they may wake them? Nope...

Enter the Message Bus

If you've ever bound an event (in the browser with jQuery or Dojo, or in applications with Java's event handlers), you've used something that passes (loosely) as a message bus. For those who don't know, a message bus is a system for transferring messages between "producers" and "consumers". One such messaging pattern is called "publish/subscribe" aka PubSub, in which producers publish messages and consumers subscribe to receive them. The subpattern I'm most familiar with is the "topic-based" system, where producers publish to a named channel (identified by a string, typically), and consumers subscribe to the same channel (and execute a callback whenever a message is published). I'm not the best source for different types of message buses or patterns, so you can read more on pubsub at the usual place.

So how does a message bus fix our problem? Easy -- the GemEncrustedSwordModel publishes an event to the "model.Sword1.unpack" channel when it is unpacked. If we had decided to destroy the view when the sword was originally packed, the ViewDatabase could have subscribed to the "model.Sword1.unpack" event so it would know when to rebuild the view. Similarly, if we had just put the view to "sleep" when the model was packed, we could have then subscribed to the "model.Sword1.unpack" event to wake itself back up. This callback would know how to get the view back into the main render loop, so paint() would begin to execute once again. "Leaving the light on", so to speak.

Interacting with Other Models

There are other uses for message buses in MVC games besides just Model->View interactions; there's also Model-Model interactions. There are times when you just don't want one model to need to know about another model. My main example here is I have a performance optimization in my grid-based game in that each Creature is associated with a Sector. Sectors are simply groups of tiles that include creatures, structures, and items. A world that's 1024x768 tiles might be 32 sectors across and 24 sectors down, assuming sectors are 32-tile squares. If the viewport is 64 tiles tall and 32 tiles wide, you can see exactly two sectors. This solution arose because of the simple problem: on which views should I call paint()? I don't want to call paint() on everything in the world; only 0.3% of the world is visible. I don't want to iterate over all views, checking to see if they're in the viewport; there could be a lot of them! However, using some simple math I can figure out which sectors are visible (upper left coordinate mod 32 will get me the sector), and I can just call paint on those. But then how do sectors know what are inside of them?

Models tell the Sector

One possible answer is whenever a model moves (changes its x/y coordinates), it looks up the sector for its old position, looks up the sector for the new position, compares them, and messages old and new sectors if necessary.

There are rarely wrong solutions to problems in programming, but this is one of them. Models shouldn't even know about Sectors -- Sectors are just a performance optimization. Models already know their own x/y coordinates, isn't that enough, without needing to know which sector they're in also? Models should be tracking only the minimum amount of state possible -- it knows where it is.

Sector Checks all Models every Tick

Another possibility is the sector checks all models supposedly within it on every tick, and if the model has since moved into another sector (that is, the model's current x/y coordinates place it outside the current sector's boundaries), remove it from the current sector's model list and add it to the next sector's model list. This would work, but it's inefficient because sectors may contain lots of models that didn't move during a given tick.

Message Bus to the Rescue (Again)

When a Model is first born into the World, the World takes the first step and tells a particular sector "this one belongs to you". At this time, the Sector subscribes to that particular Model's "move" event. Now, we only check to see if that Model moved into an adjacent Sector when it moves. Much better: the Models have no contract with Sector except to publish a "move" event when they move, and Sectors are still able to easily maintain a list of what's inside.

Side note

Admittedly, for purposes of rendering, it seems like instead of Sectors the Viewport could have simply subscribed to "move" events -- the viewport repaints if anything on screen moves. But this breaks down fairly quickly -- what if something offscreen moves onscreen? What if I want to shift the viewport one tile to the left -- it doesn't know whose "move" events to subscribe to without iterating over all the creatures in the world. There are also some model-based uses for Sectors as well, such as "what's the nearest StorageFacility in my sector?". Without Sectors, really the only other way to answer this would involve iterating over all structures belonging to the civilization.

Library Recommendation

The game I've been developing has been in JavaScript, and the message bus I've been using is called PageBus. It's standalone, 4kb minified (the lite version, that is), has an absurd amount of documentation (over 100 pages, though only ~5 are actually relevant for something like this), and it even supports topic wildcards. This means you can subscribe to all model's move events using a topic of "model.*.move", if you needed to. I haven't yet, but I definitely expect to. Highly recommended.

Hope this was informative for some people -- for me, message buses are the "missing link" to getting games to work with MVC.

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment


No trackbacks yet.