Advertisement

Understanding the Game Loop - Basix

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

Almost every game can be thought of as having one main function that contains all the game logic, and which is run either when the user does something, or after a certain amount of time. This cycle of running the same core function over and over again is called the game loop, and is crucial to understand for any sort of game development.

You've probably played the board game Chutes and Ladders (or Snakes and Ladders as we call it in the UK).

(Photo credit incurable_hippie on Flickr)

Each player rolls the die (or spins the spinner) and moves forward the number of squares indicated. The square they land on may direct them to slide backwards or climb forwards several spaces. The player wins the game when they get to the final square.

So, in the above image, landing on Square 6 makes you climb four squares to Square 10; landing on Square 19 makes you slide back 15 squares to Square 4; and landing on Square 64 means you win the game.

Now suppose you're playing single player, to practice. You do the same thing as above, over and over again, until you reach Square 64. How would you represent this in code?

You'd probably start by creating an array to store the values of the squares. Most elements would contain zero, but a few would contain either a positive number (indicating a ladder), or a negative number:

Note: This is pseudocode, not AS3

var specialSquares:Array = [];
specialSquares[0] = 0;		//the square the players start on, before 1
specialSquares[1] = 0;

...

specialSquares[6] = +4;

...

specialSquares[19] = -15;

...and so on. Then, you'd have a function to move the player based on the number on the die:

function movePlayer(squares:Number) {
	newSquare = currentSquare + squares;
	newSquare += specialSquares[newSquare];
	currentSquare = newSquare;
}

You could then put this inside a bigger function representing a whole turn:

function takeATurn() {
	diceNumber = random(1, 6);	//random number between 1 and 6
	movePlayer(diceNumber);
	if (currentSquare == 64) {
		//player has won! game over
	}
}

This function, takeATurn(), encapsulates the core game logic for single-player Chutes and Ladders. But how do we actually get this function to run? There are several options:


Run the Function Based on Player Input

We could place a button on the screen, and have it call takeATurn() each time it's clicked. If you've ever played Facebook Scrabble or Words With Friends, you've seen this option in action.


Run the Function Based on Time Passed

We could make takeATurn() run every sixty seconds.

Actually, this option is a little more complicated than it may seem. In practice we never see any games without some player input; with no interaction, it can't really be considered a game. But still, some games have an element of "calendar time" involved. Consider FarmVille: you, the player, plant your crops, and then every few minutes (or hours) they develop a little further, from seeds to shoots to fruit. And in some modes of Civilization, you're given a set amount of time (say five minutes) to make all of your moves for one "turn"; once those five minutes are up, the core game logic function is triggered.


Do Both

Some games use a mixture of these two options: for example, Mafia Wars has a resource called "energy" which refills one unit every five minutes; you take actions in the game by using this resource, so the core game logic function is still triggered by a user action, it's just restricted by time.

This is a pattern common to most games: one piece of code containing the core game logic is triggered repeatedly. We call this the game loop.

There's a term for the action or period of time that triggers the core game logic code as well: a tick (like the sound a clock makes).

So in Civilization, the tick is every five minutes. In Words With Friends, playing your turn causes a tick. In other words, the game loop runs once per tick.


What About Mario?

Super Mario Bros doesn't seem to fit into either of these categories. Mario responds to the player's input... and yet all sorts of things are going on without you needing to do anything (Goombas walk around, the timer counts down, objects fall). Are there two game loops?

No. There's just one, and it's triggered solely by time -- but with a tick of just a fraction of a second.

In Civilization, you have a period of five minutes to input everything you want to do in the current turn, before the game "ticks" and runs the game loop again based on all your input. So if you say, in Turn 23, that you want your warriors to attack a deer, then in Turn 24 everyone's getting venison for dinner.

It's the same with Mario. If you press the Jump button during one tick, then in the next iteration of the game loop, Mario will start to jump.

Note that you don't have to time your Jump press to occur just as the core game logic function is triggered -- all of your actions during a tick period are recorded and used during the next iteration of the game loop.


Is Everything Controlled Through the Game Loop?

All of the game logic is handled in the game loop. But there's more to a game than its logic, graphics being the major example.

Drawing graphics to the screen is hard work for the computer. Let's suppose you've got an action game with a tick of 1/60th of a second; that should make the game feel like it's reacting fluidly to the player's controls. But what happens if the player's computer is too slow to run all the code for the game logic (responding to input, simulating gravity, running AI routines) and draw everything to the screen within 1/60th of a second?

In this scenario, we can use two separate loops: a game loop and a draw loop. We could then run the draw loop at a much lower frequency than the game loop; let's say we refresh the screen half as often, i.e. every 1/30th of a second.

The amount of processing power required by the game may vary from level to level. Consider a shoot-'em-up: the first few levels will have very few ships on the screen, to ease the player in gently, while the last levels could have dozens of enemy ships and hundreds of bullets all flying around the same scene at once. The game loop has to figure out how all those objects should move, and the draw loop has to render each one, so while it may have been possible to run both the game loop and the draw loop every 1/60th of a second at the start of the game, by the end something has to give.

It's generally simpler to slow down the draw loop than the game loop, if you have to choose. Adjusting the game loop's tick length means adjusting everything in your game that's based on time; if Mario runs at a speed of 20 pixels/second, and you design the game with a tick length of 1/60th of a second, then you have to move him 1/3rd of a pixel per tick. If you adjust the game loop to have a tick length of 1/30th of a second, then you have to adjust this to 2/3rds of a pixel per tick -- and even that's a simple change compared to keeping physics calculations and AI routines consistent.

For this reason, games often aim to keep the game loop's tick consistent, and slow down the draw loop if more power is needed. If you've ever turned on the FPS counter (short for Frames Per Second, the number of times the draw loop is running per second) in a first person shooter, you'll have seen it change depending on how much is on the screen; the draw loop's refresh rate gets adjusted automatically. The game may look juddery -- like a live streaming video on a slow internet connection -- but unless it's being run on a computer with a lot less power than the game developers had in mind, all the objects in the game world will continue to move and interact at the correct speeds.

For a great article explaining how Flash deals with this, check out Sean Christmann's post on the 'Elastic Racetrack'.

Advertisement