This is going to be a more technical series of posts. I’m going to look at what’s going on under the hood in Delugional, and some of the approaches I took to keep the code manageable.
Outside the world of games, the longstanding best practice for building a user interface is the ‘Model View Controller’ or ‘MVC’ pattern. This pattern has spawned many variants, but the basic principle remains the same. You have a ‘model’ layer, the raw information that your application is concerned with. (Eg, this puzzle has water here and some huts here, and this many hexes, and so on.) You have a ‘view’ layer, which is responsible for showing stuff to the user, and accepting input. And finally you have a ‘glue’ layer which is responsible for passing information between the model and the view. The golden rule is that the model knows nothing about the view layer, and the view layer knows nothing about the model. The ‘glue’, implemented in various ways and known as a ‘Controller’ or a ‘Presenter’ or a ‘View Model’, exists to allow and enforce this separation.
The advantage of this approach is simple and profound: you can make changes to your model without changing your view layer, and vice versa. This isn’t such a big deal in the early stages of development, but it saves you a huge amount of pain on a large project.
Does this pattern transfer to game development – or, specifically, to Unity3D development? I admit it must be tricky to apply to, say, a 3d shooter, where the ‘model’ (eg, the game physics) and the ‘view’ (eg, Unity’s rendering) are closely intertwined at an engine level. But for something like Delugional – a puzzle game with a turn-based mechanic – it’s eminently achievable. I’ll explain how I did it.
The model layer in Delugional
This is all the puzzle-y goodness – the ‘actual game,’ as I think of it. Here are the data objects that specify puzzles, as well as components of puzzles (terrain and so forth).
Here, too, is the logic for the puzzle simulation – we decide here how the water spreads on each move, whether the puzzle is ‘won’ or ‘lost,’ stuff like that.
From a programming point of view, the classes in this layer are ‘POCOs,’ Plain Old C# Objects – they’re not dependent on Unity in any way and don’t reference the UnityEngine namespace.
The view layer
Every out-of-the-box component from Unity – Animators, Renderers, Buttons, Text components and so on – forms part of my ‘view’ layer. Here also go custom components I create that ‘live in Unityland’ (ie, they inherit from MonoBehaviour) with generic functionality that isn’t specific to my game logic. (Eg, a component whose job is to randomise the mesh used by a GameObject.) Objects in this layer don’t ‘know about’ the model layer.
The controller layer
This is the ‘glue’ layer, the go-between, the middle man. This is the code which ensures that the view reflects the game model and that the game model responds to view input.
In Delugional, classes in the controller layer are also MonoBehaviours on GameObjects. In some cases these behaviours live on a visible object, as in the case of TerrainController, which receives input and manages visual state for a single terrain hex in the puzzle. There are also higher-level controllers that live on an invisible GameObject, like PuzzleController which coordinates input and output for the whole puzzle.
This was MVC architecture for Unity. Pretty simple, eh? Of course, all this doesn’t tell you much about how to actually implement MVC in Unity. How does the controller layer ‘talk to’ the other two layers? How does it know when something in the game model changes, for instance? Take heart! In the next blog post I’ll get into a bit more detail, and talk about some useful tricks.