In this tutorial, we introduce the concept of Game Loops, and we use a game loop to create a very basic game for the LED Matrix display.
Now that we have the LED Matrix as a display and can take input with buttons, there's no reason we can't start creating some simple games. But, before we do that, there are some basics of computer game programming that are helpful to understand; in fact, once you get through this project, we'd be willing to bet that you have a new appreciation for how video games work.
Most computer games (and especially those that most kids would consider to be video games) share a common method of programming called the "game loop." A game loop is a simple concept, but is the heart of most video games. The game loop essentially works by doing the same things over and over the entire time the game is running.
We're going to explain game loops below using pseudo-code. As a reminder, here is how pseudo-code works:
Writing pseudo-code before you write software is similar to writing an outline before you write a paper or an article. Basically, you create an outline for your code using a combination of regular English mixed with some bits of programming code. This allows you (or others) to read the order of the code and determine if what you're getting ready to write is logical and will accomplish what you want and expect.
Let's use the project to show an example of how we'd write pseudo-code. In that project, we had a bunch of buttons, and we wanted to print when the buttons were pressed.
Here is what our pseudo-code might look like:
Create a list of Buttons Repeat: For each button in the list of buttons: If the button is pressed: Print which button was pressed Pause
You notice that each line is easily readable as English — with some indenting to help the readability — and overall, you now have a outline that you can use as a template to create your code.
Now, in reality, you might not need to use pseudo-code for such a simple program. But, for more complex software, writing the pseudo-code before you start writing the actual code can make the entire process much easier.
The pseudo-code for a typical game loop will look something like this:
Initialize the game environment while (game not finished): Check for user input Update the game world Draw graphics on the screen Wait for some period of time Finish & Cleanup
We're going to create a simple "game" — too simple to really qualify as a game — that will give us start to creating more complicated games later. This game is going to draw a dot on the screen and move it across the screen from left to right. That's it — like we said, pretty simple...
If you have LED Matrix hooked up, you can give this a a try now! Note that the dot will quickly move off the screen, and you'll need to manually stop the program at that point.
Lets examine the code line-by-line below.
The very first step in any game is to initialize anything that will be used in the game loop. If you're not familiar with the term "initialize," it basically means setting up all the aspects of the game so that when the user starts playing, the game code is ready to be executed. For example, this might include creating a framebuffer that will hold the graphics during the game, defining the start location and speed of various game characters, etc.
In most all Python programs, you'll start by importing the functions you're going to use in the program.
On lines 2 and 3, we import the functions/modules we'll need.
On lines 5 and 6, we:
Create a framebuffer that we'll use to draw and display the dot
Initialize the location of a dot when the loop starts (in this case, let's put the dot in the corner of the LED Matrix)
This is where the magic happens. We call this the game loop because it's an actual loop in the code that continues until the game is finished or until the user chooses to exit.
For this project, we're going to take the easy way out and just let the
program run forever (until we stop the program). We can do this using a
while True:
loop.
So, this will be the very first line of our game loop:
In a typical game loop, this is where we would check for user input — check for button presses, check for joystick movement, check for controller movement, etc. We'll do that in some subsequent projects, but for this particular project, we're keeping things simple and we're not taking any input from the user. So, there's no code to add for this part of the loop.
Updating the game world means taking the new information we have — either from the user input we've received or just because we are further along in time — and using this new information to update any parts of our game that now need to change. For example, in a character-based game, if a user has indicated that they want their character to move to the right side of the screen, the game world update might involve changing the location of the character, shifting the background, changing the location of other characters in the game, etc. This part of the game loop will figure out what the next set of graphics to display looks like in preparation to draw the new graphics.
In our example above, we're just going to automatically move the dot to the right one space, regardless of what the user does. To do that, we need to increment the X coordinate by 1.
Once we have updated all the aspects of the game world and have the information about what will get displayed next, it's time to actually display the new graphics. This typically involves erasing and drawing the new graphics to the framebuffer and then displaying that framebuffer. We can do that just like we did in the last project:
The very last thing we often do in a game loop is to wait for some period of time. This (roughly) sets the rate at which the display gets updated. By setting the frame rate, we can know exactly how much to move graphics around the display from one frame to the next.
Because our graphics will be updating each time through the loop, we'll want to choose a wait time that makes sense for our graphics updates. If not, you'll probably want to play around with various pause times and see which work the best — too slow, and you might find that your program is not responsive; too fast, and you might find that the processor wastes power and causes poor performance of other programs running on the computer at the same time.
In this case, our graphics rely on the loop speed, and we'd probably be happy moving the location of the dot every half-second, which means we'll want to put in a half-second delay at the end of the loop.
At some point, the game will probably end and we'll drop out of the game loop. This is most common when the player wins or loses a game, or when a player purposefully exits a game for some reason. When that happens, you may want to indicate to the user the final outcome of the game, play a separate set of graphics (for example, something special if the player beats the game) or you may want to go back to another menu screen.
In this project, we never drop out of the loop (remember, we used a
while True:
loop), so there doesn't need to be any code at this
point in the program.
When you run this code, you should see the dot start at one corner of the LED Matrix and move in one direction until it is off the screen. Because we're not checking to see if the dot is still on the screen or not, the code will just continue to run even after you can no longer see the dot. So, once the dot has disappeared, you'll have to stop the program manually.
Also, you might notice that the first place you see the dot is on the LED second from the left (not the very left-most dot). This is due to the fact that we "update the game world" before we "draw graphics on the screen". Our first update moves the dot one position to the right, before we've even had a chance to see it on the screen. In a more normal game, this doesn't matter, as the wait period will be fast, and this small quirk won't be noticeable.