Building Games With Python 3 and Pygame: Part 4


This is part four of a five-part series of tutorials about making games with Python 3 and Pygame. In part three, we dove into the heart of Breakout and learned how to handle events, met the main Breakout class, and saw how to move the different game objects.

In this part, we will see how to detect collisions and what happens when the ball hits various objects like the paddle, the bricks, the walls, the ceiling, and the floor. Finally, we will review the important topic of game UI and in particular how to create a menu with our own custom buttons.

Collision Detection

In games, things bump into each other. Breakout is no different. Mostly it's the ball that bumps into stuff. The handle_ball_collisions() method has a nested function called intersect(), which is used to test if the ball hit an object and where it hit the object. It returns 'left', 'right', 'top', 'bottom', or None if the ball didn't hit the object.

Hitting the Ball With the Paddle

When the ball hits the paddle, it bounces off. If it hits the top of the paddle, it will bounce back up but keep the same horizontal speed component. 

But if it hits the side of the paddle, it will bounce to the opposite side (left or right) and continue its motion downward until it hits the floor. The code uses the intersect function().

Hitting the Floor

When the paddle misses the ball on its way down (or if the ball hits the paddle on its side), the ball will keep falling and eventually hit the floor. At this point, the player loses a life, and the ball is recreated so the game can continue. The game is over when the player has run out of lives.

Hitting the Ceiling and Walls

When the ball hits a wall or the ceiling, it simply bounces back. 

Hitting Bricks

When a ball hits a brick, it's a major event in Breakout—the brick disappears, the player gets a point, the ball bounces back, and a few other things happen (sound effect and possibly a special effect too) that I'll discuss later. 

To determine if a brick was hit, the code checks to see if any of the bricks intersects with the ball:

Programming the Game Menu

Most games have some UI. Breakout has a simple menu that has two buttons that say 'PLAY' and 'QUIT'. The menu shows up at the beginning of the game and disappears when the player clicks 'PLAY'. Let's see how the buttons and menu are implemented and how they integrate with the game.

Making Buttons

Pygame doesn't have a built-in UI library. There are third-party extensions, but I decided to build my own buttons for the menu. A button is a game object that has three states: normal, hover, and pressed. The normal state is when the mouse isn't over the button, and the hover state is when the mouse is over the button but the left mouse button isn't pressed. The pressed state is when the mouse is over the button and the player has pressed the left mouse button. 

The button is implemented as a rectangle with background color and text displayed over it. The button also receives an on_click function (defaults to a noop lambda function) that gets called when the button is clicked.

The button handles its own mouse events and changes its internal state based on these events. When the button is in pressed state and receives a MOUSEBUTTONUP event, it means the player clicked the button, and the on_click() function is invoked.

The back_color property that is used to draw the background rectangle always returns the color that matches the current state of the button, so it's clear to the player the button is active:

Creating the Menu

The create_menu() function creates a menu with two buttons with the text 'PLAY' and 'QUIT'. It has two nested functions called on_play() and on_quit() that it provides to the corresponding button. Each button is added to the objects list (to be drawn) and also to the menu_buttons field.

When the PLAY button is clicked, on_play() is invoked, which removes the buttons from the objects list so they are not drawn anymore. Also, the boolean fields that trigger the start of the game—is_game_running and start_level—are set to True. 

When the QUIT button is clicked, is_game_running is set to False (effectively pausing the game) and game_over is set to True, triggering the end game sequence.

Showing and Hiding the Game Menu

Showing and hiding the menu is implicit. When the buttons are in the objects list, the menu is visible; when they are removed, it is hidden. As simple as that. 

It is possible to create a nested menu with its own surface that renders sub-components like buttons and more, and then just add/remove that menu component, but it's not needed for this simple menu.


In this part, we covered collision detection and what happens when the ball hits various objects like the paddle, the bricks, the walls, the ceiling, and the floor. Also, we created our own menu with custom buttons that we hide and show on command. 

In the last part of the series, we will look into the end game, keeping tabs on score and lives, sound effects, and music.

Then, we will develop a sophisticated system of special effects that will spice up the game. Finally, we will discuss the future direction and potential improvements.