Recreate the arcade pool action of Data East’s Side Pocket. Raspberry Pi’s own Mac Bowley has the code.
Created by Data East in 1986, Side Pocket was an arcade pool game that challenged players to sink all the balls on the table and achieve a minimum score to progress. As the levels went on, players faced more balls in increasingly difficult locations on the table.
Here, I’ll focus on three key aspects from Side Pocket: aiming a shot, moving the balls, and handling collisions for balls and pockets. This project is great for anyone who wants to dip their toe into 2D game physics. I’m going to use the Pygame’s built-in collision system as much as possible, to keep the code readable and short wherever I can.
Making a pool game
Before thinking about aiming and moving balls, I need a table to play on. I created both a border and a play area sprite using piskelapp.com; originally, this was one sprite, and I used a
rect to represent the play area (see Figure 1). Changing to two sprites and making the play area an actor made all the collisions easier to handle and made everything much easier to place.
For the balls, I made simple 32×32 sprites in varying colours. I need to be able to keep track of some information about each ball on the table, such as its position, a sprite, movement, and whether it’s been pocketed or not – once a ball’s pocketed, it’s removed from play. Each ball will have similar functionality as well – moving and colliding with each other. The best way to do this is with a class: a blueprint for each ball that I will make copies of when I need a new ball on the table.
The best part about using a class is that I only need to make one piece of code to move a ball, and I can reuse it for every ball on the table. I’m using an array to keep track of the ball’s movement – how much it will move each frame. I also need to make sure it bounces off the sides of the play area if it hits them. I’ll use an array to hold all the balls on the table.
To start with, I need a cue ball:
Aiming the shot
In Side Pocket, players control a dotted line that shows where the cue ball will go when they take a shot. Using the joystick or arrow buttons rotated the shot and moved the line, so players could aim to get the balls in the pockets (see Figure 2). To achieve this, we have to dive into our first bit of maths, converting a rotation in degrees to a pair of x and y movements. I decided my rotation would be at 0 degrees when pointing straight up; the player can then press the right and left arrow to increase or decrease this value.
Pygame Zero has some built-in attributes for checking the keyboard, which I’m taking full advantage of.
At 0 degrees, my cue ball’s movement should be 0 in the x direction and -1 in y. When the rotation is 90 degrees, my x movement would be 1 and y would be zero; anything in between should be a fraction between the two numbers. I could use a lot of ‘if-elses’ to set this, but an easier way is to use sin and cos on my angle – I
sin the rotation to get my x value and
cos the rotation to get the y movement.
I can then use those x and y co-ordinates to create a series of points for my aiming line.
Shooting the ball
To keep things simple, I’m only going to have a single shot speed – you could improve this design by allowing players to load up a more powerful shot over time, but I won’t do that here.
When the shot variable is
True, I’m going to move all the balls on my table – at the beginning, this is just the cue ball – but this code will also move the other balls as well when I add them.
Each time I move the balls, I check whether they still have some movement left. I made a
resistance function inside the
ball class that will slow them down.
Now for the final problem: getting the
balls to collide with each other and the pockets. I need to add more balls and some
Each ball needs to be able to collide with the others, and when that happens, the direction and speed of the balls will change. Each ball will be responsible for changing the direction of the ball it has collided with, and I add a new function to my
When a collision happens, the other ball should move in the opposite direction to the collision. This is what allows you to line-up slices and knock balls diagonally into the pockets. Unlike the collisions with the edges, I can’t just reverse the x and y movement. I need to change its direction, and then give it a part of the current ball’s speed. Above, I’m using a
normal to find the direction of the collision. You can think of this as the direction to the other ball as they collide.
I need to add to my
update loop to detect and store the collisions to be handled after each set of movement.
First, I use the
colliderect() function to check if any of the balls collide this frame – if they do, I add them to a list. This is so I handle all the movement first and then the collisions. Otherwise, I’m changing the momentum of balls that haven’t moved yet. I detect whether a pocket was hit as well; if so, I change the momentum so that the ball heads towards the pocket and doesn’t bounce off the walls anymore.
When all my balls have been moved, I can handle the collisions with both the other balls and the pockets:
And there you have it: the beginnings of an arcade pool game in the Side Pocket tradition. You can get the full code and assets right here.
Get your copy of Wireframe issue 36
You can read more features like this one in Wireframe issue 36, available directly from Raspberry Pi Press — we deliver worldwide. And if you’d like a handy digital version of the magazine, you can also download issue 36 for free in PDF format.