The Get Started Coding with the Raspberry Pi series of blog posts covers how to code with the Raspberry Pi through looking at code examples, with no previous software knowledge required. So far it has been seen how to position and draw shapes on the screen by using co-ordinates, using algebra-like variables, and loops to perform repetitive stuff. Bits of code were organised into groups called functions, and this simplifies the main function that starts off the program.
A library of code called pygame was introduced in the previous couple of blog posts, and it contained functions to support creating games. Graphics files (such as .png or .gif) can be displayed on the screen at specific co-ordinates. Some graphics (known as sprites) can be animated by rapidly switching in sequence the image shown on the screen. The previous blog post used this sprite technique to animate a character from an old computer game, Street Fighter, to make the character moved in a ‘ready to fight’ style. The graphics for the sprite were taken from the original Street Fighter game graphics. That blog post got us to the stage where it was straightforward to animate a character and move it on the screen in response to user interaction from the keyboard.
This blog post discusses how to create sprites from scratch, and techniques for inserting them conveniently into your own games. The game described here will be a ‘Breakout’ style typical bat-and-ball game (but the techniques apply to almost any 2D game), and will be called ‘Interference’. The plot is that there is a race against time to repair circuit boards due to an electro-magnetic pulse (EMP) that wiped out everything sometime in the 21st Century.
By the end of this blog post you should be confident to create your own animations, and animate multiple sprites on a screen and implement some game interactivity and object physics such as a bouncing ball.
This blog post will implement enough coding to animate the test probe (i.e. ball) and the robotic manipulator (aka bat), and control them. The next blog post will include things for the test probe to hit or fly over, to score points.
See the 1-minute video here:
What are Sprites again?
Sprites are small defined-size graphics that play a role in the game; either a cosmetic role (such as a background graphic or (more often) an interactive role (such as an animated game character). Sprites are usually small because it can be faster to manipulate small images on a screen than large images. Often computers or games systems have hardware acceleration built-in to improve movement and animation speed for sprites.
Where is the Code?
The rest of this blog post will describe the graphics and the code in more detail, but you can directly examine the code and edit it as desired. To obtain the code, start up the Raspberry Pi, open a terminal window and from your home folder type the following:
mkdir development cd development git clone https://github.com/shabaz123/game2.git
This will have resulted in a folder in your home folder (usually /home/pi) called development, and it will contain another folder called game2 (and a nested folder in there called assets) and the entire code and graphics files for this project will have automatically be downloaded. The file permission needs changing for the main file called interpreter.py and to do this type the following:
cd game2 chmod 755 interference.py
To run the code, now type:
To stop the game at any time, click in the terminal window and then press Ctrl-C.
Creating Sprite Graphics
See the previous blog posts for suggestions on how to do this. I used software called Pro Motion because I already had a license for this, but even Microsoft Paint could be used.
Creating a Ball Sprite
To create the game, we need some graphics. First off, a ball or ‘test probe point’ was drawn. A typical style for games is to make them cartoon-like and that can be achieved by reducing the number of colors and using very few additional colors to show shadow and highlights. I’m no artist, so I kept things simple.
It was created in a 25x25 sized grid. The purple area will become transparent; the precise color doesn’t really matter, but it is best to use the first color for it, in the palette of whatever graphics software you use. The game software will be written such that it will treat that first color as transparent. This is useful for implementing backgrounds.
Very few colors (six) were used. This is in keeping with the cartoon-type look. I saved the image in a file called ball-ss.png. Note: It is not recommended to download the graphics visible in this blog post to use in the game, because these graphics have been resized for the blog post. To get the original images at the correct size, download the code and graphics by directly typing the instructions on the Pi terminal window as shown above.
Creating a Bat Sprite
The ‘robot manipulator’ or bat sprite is more interesting; I wanted to create a long cylinder type shape with a revolving effect, and that was implemented by shading a horizontal line in a lighter color, and moving it from the top to the bottom during the animation. In total, ten frames of animation were created. These could all have been saved as separate graphics files but it gets messy to maintain, especially when there could be dozens of other sprites in the game. You’d soon end up with hundreds of graphics files for the animations.
A solution is to place all of the frames of animation (for either a single sprite, or multiple ones) into a single graphics file, and get the software to chop it up internally in memory. The single graphics file is known as a sprite sheet.
I created all ten graphics images in the same file next to each other. Each image was of size 75x13, so the overall image size ended up being 750x13 pixels since there were ten frames of animation. Each frame is near-identical apart from the lighter horizontal line mentioned earlier; this is a cartoon-like way of showing a cylindrical object that is turning on its axis.
A closer view of the first few frames shows the horizontal bar:
The file is called bat-ss.png.
Creating a Background
I took an EAGLE CAD drawing I’d done a while ago, and resized it to the game screen resolution (320x200 in my case), and reduced the number of colors using a graphics program.
The filename is level1-stage.gif. Future game levels could have different graphics files.
Working with Sprite Sheets
As mentioned, any sprite graphics that need to be animated can be saved in a single file called a sprite sheet. Sprite sheets are not essential, but makes things cleaner than having to deal with so many individual files.
A short bit of code found online called spritesheet.py was used to internally split up the source sprite sheet file. The code is reproduced here. The load_strip function basically accepts a filename, and the co-ordinates of the first frame in the graphics file, and the number of frames, and the index for the desired color to be made transparent. The code will return a set of smaller graphics objects (called Surfaces) containing each frame.
Implementing the Bat
Code very similar to the previous blog post was reused, but with some slight alterations to handle obtaining the multiple frames of animation from a sprite sheet. If you look at the ken.py file from the previous blog post, you’ll see that bat.py is near-identical.
Implementing the Ball
The code to handle the ball is again based on the same file as before, but with some additions. The aim was to try to keep things self-contained and therefore encapsulate as much of the ball behaviour in the ball.py file as possible. As a result, the file contains some useful functions that are used to handle ball behaviour. A couple of variables (ballxx and ballyy) define the direction that the ball is traveling in. They only take the values +1 and -1 (and possibly zero). If ballxx is set to +1 then that means that the ball is moving from left to right. If ballyy is set to +1 then that means the ball is moving from top to bottom. If both are set to +1 then the ball is moving diagonally, and so on.
There is code in a function called collision, which gets called whenever the ball touches the bat. When that happens, the ball is sent bouncing in a different direction and that is achieved by simply modifying the ballxx and ballyy values to represent the new direction. Sometimes the ball needs to be accelerated or decelerated (for instance if the bat is moving toward the ball when the ball hits it, then some additional energy can be given to the ball). The actual speed in the horizontal and vertical direction is represented by two variables called ballxdel and ballydel, and they take a positive number. The higher the value, the slower the ball is traveling in that direction. The fastest (lowest) value is 1. The rules on when to accelerate the ball, and the direction the ball should bounce off the bat, are crude and are implemented in the collision function (in future the rules could be refined further); the top central part of the bat is treated differently to the sides and corners of the bat when it comes to deciding which way to bounce the ball and at what speed (which depends on the bat speed and direction of travel).
The entire code isn’t shown here, only the collision function. It looks lengthy, but can be followed by examining it portion-by-portion. The code is run whenever a collision has occurred, and the code needs to determine if the ball hit the top surface of the bat, or the side. Line 59 checks the height of the ball on the screen and compares it to the height of the bat on the screen. If the ball is lower than the top of the bat, then it means that the detected collision cannot have been to the top surface, but perhaps to the side of the bat. That determines whether the code from line 60 onwards is run, or if the code from line 74 onwards needs to be run. The speed_up and speed_down functions manipulate the ballxdel and ballydel values (examine the downloaded source code to see this).
So, some variables such as ballxx, ballyy, ballxdel and ballydel represent the direction of the ball travel and the speed. The actual movement of the sprite is contained in the update function (again, this reuses code created in the previous blog post but extends it).
The update code fragment is shown below. It executes a function called movepos whenever it is desired to push the sprite rect area by one pixel in any direction, by passing it the ballxx and ballyy variables.
The ball rect position is compared with the screen width and height too, in order to bounce the ball off the edges of the screen.
The main function is contained in the file interference.py and it is based on the sf.py file from the previous blog post. As a brief description, if you examine the downloaded source code you'll see that lines 39-47 are responsible for creating the ball_sprite and bat_sprite objects and putting them in a couple of groups. A list of the areas of the screen which they occupy is also stored; this is used to selectively copy the background there whenever the sprite moves. The areas are known as Rect objects when using the pygame library. They store rectangular area information (the co-ordinates of the top-left corner of the area, and the width and height of the area).
Similar to the previous blog post, there is a forever loop that checks for button-presses. A collide_rect function in pygame is used to see if any particular sprite has hit another one; it works by examining the Rect object associated with each sprite and seeing if there is any overlap. The code (on line 69) checks if the bat and ball have hit. If there is a collision then the function described further above (called collision) from the ball.py file is executed, and that will change the direction and speed of the ball! Periodically the main code will execute the update function mentioned earlier, and that will adjust the Rect object depending on the values in the ballxx, ballyy, ballxdel and ballydel variables.
That’s it! The main code is short and simple, because the rules that handle the ball direction, speed and behaviour when it collides are all encapsulated in the ball object’s file (ball.py).
We are now closer to a complete game! The concept of sprite sheets was described, and how it can make it easier to work with sprites. The method of creating cartoon-like sprites was also described, using highlights and shadows, and a moving line to represent a rotating cylindrical manipulator or bat.
Collision detection is handled by seeing if the rectangular areas that the sprites occupy are overlapping or not; this is handled using the collide_rect function in the pygame library.
The physics of an object were implemented by deciding in which direction the ball should move and speed, and ultimately updating the Rect object which defines the area on the screen where the sprite will be drawn by the pygame library.
I hope the blog post is useful for creating custom games, do share you games or demos : )