Suppose we’re playing a game of chess. Your pawn is at E2 and you want to move it to E4, so you pick it up and put it on that square. E2 is a legal position and so is E4. But, technically speaking, the space between those points is not a legal position. When you moved the pawn you actually moved it through many illegal positions in 3D space before finally placing it on the desired square.
Of course, this doesn’t really matter. Humans are smart and can easily understand that we just need to wait for the player to finish moving the pawn. But computers, on the other hand, are very literal. They will not understand this idea unless you program them to understand it.
For this reason, programming a turn based game is in some ways much harder than programming a real-time game! In a real-time game like FIFA, you could say a “turn” is one frame. So basically, the game has 60 turns per second and the footballers will never actually be in an illegal position. But in a turn-based game like chess (or Patch Quest), there needs to be a distinction between a piece’s legal and animated positions.
Splitting Legal and Animated State
The code for Patch Quest is roughly split into 3 parts:
- The Model keeps track of the current state of the game.
- The View displays that state on your screen.
- The Controller looks at user input and tells the model how to change.
The Model is further split into 2 halves, the “State” and the “Animator”. The state is always in a legal position, while the animator can be in all sorts of positions regardless of legality. The state changes from one position to another instantly, whereas the animator changes slowly over time. As an example, let’s consider the Model for a creature in Patch Quest.
A creature is always positioned on a tile and they can run from one tile to another on their turns. When this happens, the state is instantly updated. So one one frame the creature is on tile A and the next frame that creature will be on tile B. It will never be in an illegal position between two tiles.
When the state is updated it also sends an animation to the global animation queue. This queue plays the animations one-by-one and in turn moves the creature’s Animator. If it takes 1 second for the creature to run from A to B then the Animator will be moved through 60 intermediate positions, none of which are legal. But that’s OK because the Animator can go wherever it wants and doesn’t need to worry about legality.
If the game needs to know what tile a creature is on it asks the State. But when the game needs to know where to display the creature on-screen it asks the Animator. Because of these 2 halves, each game piece can be in a legal position and a different animated position at the same time.
Constructing the View
The View looks at the Animator half of the model and creates graphics to represent it. Every frame it checks to see if the Animator has changed and, if it did, updates the graphics to match it. The major benefit of keeping your Model and View separate is it lets some of your Model have no View and it also lets some of your View have no Model.
For example, Patch Quest tasks you with exploring an island. The island is pretty big (and getting bigger with each update) but only a tiny portion of it is on-screen at any point in time. Creating and updating the graphics for the entire island would be a waste because you wouldn’t see most of it! By keeping the Model and View separate we can create the Model for the entire island but only create a View for the bits currently on-screen.
Similarly, we can also view things that aren’t part of the Model. For example, imagine that when the player highlights a creature we want to display where it might move to (let’s call it position C). The creature is currently at A, not C, so we can’t display it there. But we could create a custom Animator positioned at C and create the view for that, instead.
This is done using a “View Source” interface. If you’re not a programmer, an interface is basically like an adapter for power outlets. USA plugs fit into USA power outlets and UK plugs fit into UK power outlets. But if you have the right adapter, you can fit any plug into any outlet. In this case, we’re creating an “adapter” for the Animator, so we can plug the view into either the real Animator or a custom one with whatever data we want inside it. So this way, we can use the same View code to display the game’s current animated state and also any other custom state we might want.
Handling User Input
The player will click the mouse and push buttons on the keyboard, but what these things actually do depends on the context of your game’s current state.
First of all, when the player clicks the mouse we need to figure out what they actually clicked on. This is done by “raycasting”, sending out a ray from the mouse position and seeing which View element it collides with. If the ray hits something, that information is then sent to the Controller which tries to figure out what that click actually means. Similarly, events from the keyboard are sent to the Controller.
The Controller takes in these mouse and key events, looks at the current game State, and tries to figure out what change to make. If the change is legal it will be accepted by the State.
Putting It All Together
This closes the loop! The Controller receives input events and updates the State, which in turn animates the Animator. The View reads from the Animator and updates the game’s graphics accordingly. The View also helps provide mouse events to the Controller. Here’s a big diagram with all this information in one place.
Each “piece” of the game (in Patch Quest these are the Patches, Tiles, Creatures, Scenery, Items and more) has its own State, Animator and View. The other stuff is “global”, meaning just one of them exists.
In summary, I feel this design has a bunch of advantages. Here are some:
- The State is simpler to reason about because it’s always in a legal position.
- You can queue as many fancy animations as you want without worrying how it affects the State.
- You can view things that aren’t part of the State, and you can have some State that isn’t viewed.
I should take this chance to point out that this is only one way of programming a turn-based game. It most likely isn’t the best way, either! But it’s the way I’ve programmed my own game and I feel pretty good about it. I had to piece a lot of this stuff together in my first year of development which was a slow process full of detours and dead-ends. I’m hoping that this article can give new developers a foundation for programming their own turn based game in a way that is simple, flexible and scalable.
The next update for Patch Quest will release in two weeks! I’ve added a bunch of new features and I’m very excited to share them with you all.
Have a question? Want to comment? Head over to the forums. I’d love to hear from you.