Creating Games in Python 3 and Pygame: Part 2
- Transfer
- Tutorial
(The rest of the tutorial: first , third , fourth , fifth .)
In the second of five parts of the tutorial on creating games using Python 3 and Pygame, we will look at the class
TextObject
used to render text on the screen. We will create the main window, including the background image, and then learn how to draw objects: bricks, ball and racket.Class TextObject
The class is
TextObject
designed to display text on the screen. We can conclude that from the point of view of design, it should be a subclass of the class GameObject
, because it is also a visual object and it also needs to be moved sometimes. But I did not want to introduce a deep class hierarchy, in which all the Breakout text displayed on the screen remained unchanged. The class
TextObject
creates a font object. It renders the text on a separate text surface, which is then copied (rendered) to the main surface. An interesting aspect TextObject
is that it does not have any fixed text. It gets a function text_func()
called every time it is rendered.This allows us to update the display of lives and points in Breakout, simply by creating a function that returns current lives and points, rather than tracking which text objects display points and lives and updating their text each time they change. This is a convenient trick from functional programming, and in large games it allows you to maintain the convenience and accuracy of the program.
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 a main window
Games on Pygame run in windows. You can even make them run in full screen mode. Now I will tell you how to display an empty Pygame window. You will see many of the elements that we discussed earlier.
init()
Pygame is called first , and then the main drawing surface and timer are created. Then the main loop is executed, which constantly fills the screen with a solid gray color and calls the timer method
tick()
with a 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 plain background color does not look very interesting. Pygame works very well with images. For Breakout, I found a curious photograph of real space taken by NASA. The code is very simple. First, before the main loop, it loads the background image using the function
pygame.image.load()
. Then, instead of filling the screen with color, it performs blitting (copying of bits) of the image on the screen to the position (0,0). As a result, an 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 module
pygame.draw
has functions for rendering the following figures:- rectangle
- polygon (polygon)
- circle (circle)
- ellipse (ellipse)
- arcs (arc)
- line segment
- segments (lines)
- smooth line (anti-aliased line)
- anti-aliased lines
All objects in Breakout (excluding text) are simple shapes. Let's learn the draw () method of various Breakout objects.
Brick drawing
Bricks are just rectangles. Pygame has a function
pygame.draw.rect()
that takes a surface, color, and a Rect object (left and top coordinate, width and height) and renders a rectangle. If the optional width parameter is greater than zero, then it draws a path. If the width is zero (the default value), then draws a solid rectangle. It is worth noting that a class
Brick
is a subclass GameObject
and receives all its properties, but also has a color that it processes on its own (because there may be game objects that have several colors). special_effect
We will not consider the field yet.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)
Ball drawing
The ball in Breakout is just a circle. Pygame has a function
pygame.draw.circle()
that receives color, center, radius, and an additional width parameter, which is zero by default. As in the function pygame.draw.rect()
, if the width is zero, then a solid circle is drawn. Ball is also a subclass of GameObject. Since the ball always moves (unlike bricks), it also has a speed that is transmitted for processing to the base class
GameObject
. The Ball class has a slight difference - the x and y parameters indicate its center, and the x and y parameters passed to the base class GameObject
are the upper left corner of the bounding box. To convert the center to the upper left corner, just subtract 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)
Racket drawing
A racket is another rectangle that moves left and right in response to a player pressing the arrow keys. This means that the position of the racket in different frames may differ, but in the process of drawing it is just a rectangle, which should be rendered in the current position, whatever it may be. Here's what the corresponding code looks like:
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, we learned about the TextObject class and how to render text on the screen. We also learned how to draw objects: bricks, ball and racket.
In the third part, we will learn how event processing works and how Pygame allows us to intercept events and respond to them (keystrokes, mouse movement and mouse clicks). We will also consider such elements of the gameplay as the movement of the ball, setting its speed and moving the racket.