Crap. That was a mouthful.
There seems to be a huge amount of stuff on Her Majesty’s Interwebs about all of these topics, but very little explaining how to pull them all together into something coherent. There’s a shit-ton of bad code to copy to get something bouncing around on screen, but none of it cares about scaling well into anything complex. And there seems to be a massive dearth of diagrams showing how anything connects together, whether it’s classes within libraries, or how you’re supposed to fit them together in your own code. I don’t know about you, but I can’t design anything without diagrams, so expect lots here. In fact, let’s have one now.
(If you want to sketch diagrams like this quickly to help you design your code, try Pidgin. It’s a cool little tool that lets you write out relationships and it does the drawing-y bit.)
Or perhaps me.
CoffeeScript comes with it’s own cons of course: it’s compiled, not run natively in browsers, so that means you have to have a build process. You can either have the build done by the server so you actually deploy CoffeeScript, or you can build as part of testing and deployment.
EaselJS and Box2D libraries
I’ve chosen to work with EaselJS (part of the CreateJS suite) and Box2Web (for now) but the real challenge here is actually independent of the choice of libraries. It’s not about which one you choose to render graphics and which one you use to handle physics, but how to combine them well. A decent game architecture should allow you to switch either fairly easily, and also port your game to different platform, like a specific mobile device (iOS/droid) or a platform-independent SDK such as Corona.
The Model-View-Controller Pattern
This is where MVC comes in. Model-View-Controller is the architectural design pattern adopted by web frameworks like Ruby on Rails and CakePHP. It’s more of a principle than a specific architecture, since it can take different forms depending on what you’re actually building. A game is not a web application built around a relational database, so the architecture of a game isn’t going to look exactly the same as a web system. However, the real principle at the heart of MVC is Separation of Concerns.
Now, I’m not actually an architecture purist – I firmly believe there is a balance to be had between writing beautifully architected code and getting something built and shipped. I am a big fan of Lean. There is absolutely nothing wrong in my view, in hacking some hideous Frankenstein of code together to get something working and out there. Software architecture isn’t like building architecture; its beauty is not on display for all to admire. I do think though, that once you have something out there, it is worth thinking about how you can refactor it so that the code is easier to maintain and scale. (It’s not even about code re-use. To paraphrase Matz, it’s not about achieving perfection, it’s about how you feel when programming. I don’t mind hacking prototypes to get something working or to learn how something works, but I hate working with that code afterwards.)
Productivity not purity
MVC comes with its own overhead in terms of number of classes, complexity and messages being thrown around. It’s interesting that MVC hasn’t been widely adopted (apparently) in games industry projects is cited as A) performance (probably true) and B) the fact that the Model in games is often “the same as the View” (partially true e.g. if you have a dedicated GPU for rendering then you may well rely on its ability to other fast vertex operations like collision detection which breaks MVC completely). In a web game though, the model can be properly separated from the View.
Remember though, it’s not a perfection or purity of this paradigm but getting the benefits of the underlying principles of Separation of Concerns. Architecture is a function of project scale, not just the technologies involved: If you’re planning something tiny, it’s certainly overkill to go for a fully OOP and MVC design, but you should still about separating concerns and writing modular, DRY code. The great thing about libraries like EaselJS and Box2DWeb are that they are already dealing with separate domains. EaselJS is all about the View. Box2D is all about the Model.
So, is your game code the Controller? Hmm, no. It’s all three, and mostly Model, in fact. We need to understand what MVC really means. What is each part concerned with and how do they interact? I keep seeing diagrams showing MVC that are wrong. Let’s fix that now:
However the responsibilities of Models, Views and Controllers, the dividing lines of the separation of concerns is very different in a game than it is in a web-application. The decision about what goes where. For example, visual effects, like particles, will probably use the physics library but really they’re View code because they’re not truly part of the game Model. The game is independent from them. They’re visual gloss. They could be rendered completely differently without affecting the game.
Controller classes are all about handling action input from the User. This can get a little fuzzy when we consider that this can include input from elements from the screen, e.g. like on-screen buttons, but remember even when the user taps a button on screen, it’s the touchscreen on top of the View that’s handling the touch. Controller classes are responsible for responding to the User’s desires and making the internal game state change appropriately. This includes creating the game world in the first place when the User clicks ‘play’. Controllers don’t necessarily fire events or call methods on Models – they can also be polled to get their state. You can use both: i.e. your game start button’s click event can call Game.start() but during the game, you might want to know if the left key is down or not during a game tick to decide whether to apply a force left to your hero. You poll the keyboard controller for this information.
The Model classes are all about representing the internal state of the game and the logic about changing state. So that’s the physical world itself and its contents, and all game elements in play. This is more than just the physics; physics is just part of the game logic. Notice that although the Model is not concerned with rendering, it does “update” the View. All this means is that it is responsible for the telling the View what’s changed.
View classes are only concerned with rendering things so the user can see them. We should also include in the View anything sense-related actually, so that means audio too.
To MVC or not to MVC…
If you’re planning to port your game to run on different platforms, or you’re not sure about which libraries you’re going to use for physics or rendering, then you will probably want to be very clean about keeping these domains decoupled. In other words, you will want write your own classes to wrap around any library classes. Remember, we’re not just Separating Concerns for fun, we do it because it will make life easier and coding more enjoyable. If it is going to make things harder overall, don’t do it!
In a (business) web application, separating the model and view is easy because there’s usually nothing visual about the business model. Games are intrinsically visual: the view and model are coupled, and there’s no escaping this. It’s a question of loose-coupling versus tight-coupling.
If, for example, we create a BallModel class that contains a Box2D ball body, and BallView class that contains the EaselJS Bitmap. Any calls from BallModel to update the view go via BallView, so your model is now competely agnostic about which library you choose. If we have these inherit from base classes that do the common stuff, then we can benefit from Polymorphism. So we associate our base ObjectModel class with a base ObjectView class and
ObjectModel.update() can call
ObjectView.update() which will do standard transformations of the physics coordinates to pixels (say).
Now if we decide to switch to a different graphics library, our code changes are all (hopefully) contained in our View classes. The same goes for your physics engine. You will need one class at least that knows about both in order to convert from model units (kg/metres/seconds) into view units (pixels). This could be a helper, but it’s also reasonable to have it handled in the View. It’s the View’s job to render the Model.
MVC in games therefore is going to look broadly like this:
Controllers will kick off the game and handle user input during it. The models will concerns themselves with the (internal/invisible) world of the game, and the views will make it all visible to the user. Each of these domains (MVC) should have a nice class hierarchy for nice modular code, not just our own code, but 3rd-party library code.
This is all very abstract, let’s have look with some concrete examples to see what the challenge is working with real libraries.
First off, EaselJS. EaselJS is a brilliant library that makes graphics and sound in HTML5 really easy. It interacts with a <canvas> element in your HTML document.
I’m using “is” here to show inheritance. I’m only showing the core visual classes here, there are dozen more that do all sort of fancy things like image filters, event handling etc. I’ve also included some other libraries provided by CreateJS as you’ll almost certainly need them. You can just include the EaselJS library on its own if you prefer.
Easel has a Stage which represents the Canvas in your HTML doc. It is (inherits from) a Container which both is and can contain DisplayObjects. There are classes like Bitmap and Sprite (animated bitmaps) which are DisplayObjects. The documentation for EaselJS is excellent although lacking lovely diagrams like this one to help see how it fits.
Now let’s look at Box2D. The architecture of Box2D is pretty much platform-independent so it shouldn’t matter which port you use, or whether you read the (excellent) C++ manual even though we’re using JS/CoffeeScript. Box2D is a fully-fledged rigid body physics engine. It’s complex. I’m going to assume you’re fine with that. I’m not going to be covering all the details of Box2D here, only the bits needed to work out how to create an HTML5 game architecture with it and EaselJS.
So, while Box2D is actually in three modules (Common, Collision and Dynamics) I’m just going to look at a small part of the Dynamics to get started.
Box2D worlds are the corollary of our Stage in EaselJS (but can be bigger). Then we have bodies which are rather more complicated than our Bitmap / Display Objects from EaselJS. Box2D separates physical concerns into three classes for objects.
Bodies are concerned with having positions, moving about, rotating, responding to forces etc. but they’re a bit like ghosts; they don’t have a shape or material.
Fixtures add material properties to a body (so it’s like telling a body what it’s made of, how dense the material is and how springy for example). Fixtures also have a Shape, which in turn tells the body how large it is.
Shapes are all about collisions. They determine the edges and extent of bodies and therefore how they interact with other bodies/shapes. Shapes are also the most tightly-coupled concept in our Model to our View, because normally our graphics will need to match our shapes. There’s no escaping this. It’s nothing to worry about. Our models are responsible for creating and updating their views. I.e. initiating and causing those events. They’re just not responsible for the details of what happens inside them.
Notice that a body can have more than one fixture. I.e. you can create compound bodies by fixing (say) a rectangle and circle to a body. These two shapes will never move with respect to each other.
We don’t actually need to add anything else to get a basic physical system going. We could add Constraints and Joints to our world to remove degrees of freedom or join bodies together, but let’s leave that for now. It’s detail we’re not concerned with yet.
The Box2D world doesn’t come with its own ticker. Once we setup the rules (gravity etc.) we call
world.Step() with some parameters to run it one timestep. This ticker is provided by EaselJS, so here the GameApplication View will be asking the model to update itself. The model in return will update all of it’s entities and they will all communicate those updates back to their Views.
Hopefully, you’re starting to get a clear sense of how to organise your code.
In part 2, I’m going to get into the details of a class hierarchy that brings all this together with examples.