Overview
This is part two of a five-part series of tutorials about making games with Python 3 and Pygame. In part one, I introduced the series, covered the basics of game programming, introduced Pygame, and examined the game architecture.
In this part, we’ll look at the TextObject
class used to render text on the screen. We’ll create the main window, including a background image, and then we’ll learn how to draw objects like bricks, the ball, and the paddle.
The TextObject Class
The TextObject
class is designed to display text on the screen. The case could be made from a design point of view that it should be a sub-class of GameObject
as it is also a visual object and you may want to move it. But I didn’t want to introduce deep class hierarchies, when all the text that Breakout displays on the screen stays put.
The TextObject
creates a font object. It renders the text into a separate text surface that is then blitted (rendered) onto the main surface. An interesting aspect of the TextObject
is that it doesn’t have any fixed text. Instead, it gets a function called text_func()
that is called every time it renders.
This allows us to update the display of lives and score in Breakout just by providing a function that returns the current lives and current score, instead of keeping track of which text objects display lives and score and updating their text every time they change. This is a neat trick from functional programming, and for larger games it can help you keep everything nice and tidy.
import pygame class TextObject: def __init__(self, x, y, text_func, color, font_name, font_size): self.pos = (x, y) self.text_func = text_func self.color = color self.font = pygame.font.SysFont(font_name, font_size) self.bounds = self.get_surface(text_func()) def draw(self, surface, centralized=False): text_surface, self.bounds = self.get_surface(self.text_func()) if centralized: pos = (self.pos[0] - self.bounds.width // 2, self.pos[1]) else: pos = self.pos surface.blit(text_surface, pos) def get_surface(self, text): text_surface = self.font.render(text, False, self.color) return text_surface, text_surface.get_rect() def update(self): pass
Creating the Main Window
Pygame games run in windows. You can make them run fullscreen too. Here is how you display an empty Pygame window. You can already see many of the elements I discussed earlier. First, Pygame init()
is called, and then the main drawing surface and the clock are created.
Next is the main loop, which consistently fills the screen with uniform gray and calls the clock tick()
method with the frame rate.
import pygame pygame.init() screen = pygame.display.set_mode((800, 600)) clock = pygame.time.Clock() while True: screen.fill((192, 192, 192)) pygame.display.update() clock.tick(60)
Using a Background Image
Usually, a uniform color background is not very exciting. Pygame does images very well. For Breakout, I splurged and went for a fancy real space image from NASA. The code is very similar. First, just before the main loop, it loads the background image using the pygame.image.load()
function. Then, instead of filling the screen with color, it “blits” (copy the bits) the image to the screen at position (0,0). The effect is that the image is displayed on the screen.
import pygame pygame.init() screen = pygame.display.set_mode((800, 600)) clock = pygame.time.Clock() background_image = pygame.image.load('images/background.jpg') while True: screen.blit(background_image, (0, 0)) pygame.display.update() clock.tick(60)
Drawing Shapes
Pygame can draw anything. The pygame.draw
module has functions for drawing the following shapes:
-
rect
-
polygon
-
circle
-
ellipse
-
arc
-
line
-
lines
-
anti-aliased line
-
anti-aliased lines
In Breakout, all the objects (except the text) are just shapes. Let’s look at the draw() method of the various Breakout objects.
Drawing Bricks
Bricks are bricks. They are just rectangles. Pygame provides the pygame.draw.rect()
function, which takes a surface, a color, and a Rect object (left, top, width and height) and renders a rectangle. If the optional width parameter is greater than zero, it draws the outline. If the width is zero (which is the default), it draws a solid rectangle.
Note that the Brick
class is a subclass of GameObject
and gets all its properties, but it also has a color it manages itself (because there may be game objects that don’t have a single color). Ignore the special_effect
field for now.
import pygame from game_object import GameObject class Brick(GameObject): def __init__(self, x, y, w, h, color, special_effect=None): GameObject.__init__(self, x, y, w, h) self.color = color self.special_effect = special_effect def draw(self, surface): pygame.draw.rect(surface, self.color, self.bounds)
Drawing the Ball
The ball in Breakout is just a circle. Pygame provides the pygame.draw.circle()
function that takes the color, center, radius and the options width parameter that defaults to zero. As with the pygame.draw.rect()
function, if the width is zero then a solid circle is drawn. The Ball is also a derived class of GameObject.
Since the ball is always moving (unlike the bricks), it also has a speed that is passed on the GameObject
base class to be managed. The Ball class has a little twist because its x and y parameters denote its center, while the x and y parameters passed to the GameObject
base class are the top-left corner of the bounding box. To convert from center to top-left corner, all it takes is subtracting the radius.
import pygame from game_object import GameObject class Ball(GameObject): def __init__(self, x, y, r, color, speed): GameObject.__init__(self, x - r, y - r, r * 2, r * 2, speed) self.radius = r self.diameter = r * 2 self.color = color def draw(self, surface): pygame.draw.circle(surface, self.color, self.center, self.radius)
Drawing the Paddle
The paddle is yet another rectangle that is indeed moving left and right in response to the player’s pressing the arrow keys. That means that the position of the paddle may change from one frame to the next, but as far as drawing goes, it is just a rectangle that has to be rendered at the current position, whatever that is. Here is the relevant code:
import pygame import config as c from game_object import GameObject class Paddle(GameObject): def __init__(self, x, y, w, h, color, offset): GameObject.__init__(self, x, y, w, h) self.color = color self.offset = offset self.moving_left = False self.moving_right = False def draw(self, surface): pygame.draw.rect(surface, self.color, self.bounds)
Conclusion
In this part, you’ve learned about the TextObject class and how to render text on the screen. You’ve also got familiar with drawing objects like bricks, the ball, and the paddle.
In the meantime, remember we have plenty of Python content available for sale and for study in the Envato Market.
In part three, you’ll see how event handling works and how Pygame lets you intercept and react to events like key presses, mouse movement, and mouse clicks. Then, we’ll cover gameplay topics like moving the ball, setting the ball’s speed, and moving the paddle.
Powered by WPeMatico