Managing Game State in Breakout
Sun, Jun 12, 2011Switching from Pong to Breakout introduces a little more complexity in the game state, with scores, highscores, lives, levels and more.
So it becomes worthwhile to invest a little time in some better coding patterns.
Breakout uses the same basic Game.Runner that was abstracted from my earlier pong game here, with some minor changes:
- Some additional DOM manipulation methods.
- A Math helper module for move, accelerate and intercept methods.
- A StateMachine helper class.
The biggest change is the introduction of the state machine which helps keep the game code from degenerating into a complex spaghetti mess.
State vs State
First, we need to clarify some terminology. Depending on the context, when we say game state we could mean one of 2 things:
- game values - e.g. positions, lives, scores, level, etc.
- game mode - e.g. playing, paused, menu etc
A finite state machine is used to manage the second case, the game mode. It automatically generates methods to transition between the game modes and provides events for custom code for when those transitions occur. It is during these events that the game values can be cleared or set.
Typically, when I talk about the game state, I’m refering to the second case, that which is controlled
by the finite state machine, e.g. the game mode. If I want to refer to the state of some internal
game variable I will be specific and say something like number of lives
, or current position
.
For example. The play()
method might be used to transition from the menu
state into the game
state, during which time the current values for score, lives, level etc can be set.
Confused yet ? Great, then lets move on…
Breakout State Machine
For a simple game like breakout I can start off with 2 simple states:
- menu - waiting for the user to start the game
- game - the user is playing the game.
There is a single event play
to get from menu to game state, but there are 2 different events that can
be used to transition in the other direction, abandon
and lose
. The FSM is constructed with the
following simple configuration:
state: {
initial: 'menu',
events: [
{ name: 'play', from: 'menu', to: 'game' },
{ name: 'abandon', from: 'game', to: 'menu' },
{ name: 'lose', from: 'game', to: 'menu' }
]},
The state machine takes care of some infrastructure and provides us with:
play()
- transition from ‘menu’ to ‘game’abandon()
- transition from ‘game’ to ‘menu’lose()
- transition from ‘game’ to ‘menu’is(s)
- return true if states
is the current state
This allows us to simply hook into the state machines events in order to set the internal breakout game state values:
onmenu: function() {
this.resetLevel();
this.paddle.reset();
this.ball.reset();
},
ongame: function(delay) {
this.score.reset();
this.ball.reset();
},
onleavegame: function() {
this.score.save();
this.score.resetLives();
},
onbeforeabandon: function() {
return this.runner.confirm("Abandon game?")
},
This kind of infrastructure allows our game to stay a clean, event driven application instead of devolving into a complex procedural set of if/then/else statements.
Object State Machines
In addition to the high level game state (menu
vs play
), any individual object within a game can also
benefit from being a finite state machine. For example, in a game like gauntlet, the individual baddies
might have ‘idle’, ‘searching’, ‘chasing’, ‘dying’ states.
However, in a simple game like Breakout, this wasn’t necessary, so thats a strategy I’ll have to wait until I tackle a more complex example….
Related Links
You can find the game here and the code is here