(Остальные части туториала: первая, третья, четвёртая, пятая.)
Во второй из пяти частей туториала, посвящённого созданию игр с помощью Python 3 и Pygame, мы рассмотрим класс
TextObject
, используемый для рендеринга текста на экране. Мы создадим основное окно, в том числе и фоновое изображение, а затем научимся отрисовывать объекты: кирпичи, мяч и ракетку.Класс TextObject
Класс
TextObject
предназначен для отображения текста на экране. Можно сделать вывод, что с точки зрения дизайна он должен быть подклассом класса GameObject
, потому что тоже является визуальным объектом и его тоже иногда нужно двигать. Но я не хотел вводить глубокую иерархию классов, при которой весь отображаемый Breakout текст оставался на экране неизменным.Класс
TextObject
создаёт объект шрифта. Он рендерит текст на отдельную текстовую поверхность, которая затем копируется (рендерится) на основную поверхность. Интересный аспект TextObject
заключается в том, что у него нет какого-то фиксированного текста. Он получает функцию text_func()
, вызываемую каждый раз, когда он рендерится.Это позволяет нам обновлять отображение жизней и очков в Breakout, просто создав функцию, возвращающую текущие жизни и очки, а не отслеживать то, какие текстовые объекты отображают очки и жизни и обновлять их текст при каждом их изменении. Это удобный трюк из функционального программирования, и в крупных играх он позволяет поддерживать удобство и аккуратность программы.
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
Создание основного окна
Игры на Pygame выполняются в окнах. Можно даже сделать так, чтобы они выполнялись в полноэкранном режиме. Сейчас я расскажу, как отобразить пустое окно Pygame. Вы увидите многие элементы, которые мы обсуждали ранее. Сначала вызывается
init()
Pygame, а затем создаются основная поверхность рисования и таймер.Затем выполняется основной цикл, который постоянно заполняет экран однотонным серым цветом и вызывает метод таймера
tick()
с частотой кадров.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)
Использование фонового изображения
Обычно однотонный цвет фона выглядит не очень интересно. Pygame очень хорошо работает с изображениями. Для Breakout я нашёл любопытную фотографию настоящего космоса, сделанную НАСА. Код очень прост. Сначала он перед основным циклом загружает фоновое изображение с помощью функции
pygame.image.load()
. Затем вместо того, чтобы заливать экран цветом, он выполняет блиттинг (копирование битов) изображения на экран в позицию (0,0). В результате на экране отображается изображение.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)
Отрисовка фигур
Pygame может рисовать всё, что угодно. В модуле
pygame.draw
есть функции для отрисовки следующих фигур:- прямоугольника (rect)
- многоугольника (polygon)
- круга (circle)
- эллипса (ellipse)
- дуги (arc)
- отрезка (line)
- отрезков (lines)
- сглаженного отрезка (anti-aliased line)
- сглаженных отрезков (anti-aliased lines)
Все объекты в Breakout (за исключением текста) являются простыми фигурами. Давайте изучим метод draw() различных объектов Breakout.
Отрисовка кирпичей
Кирпичи — это просто прямоугольники. В Pygame есть функция
pygame.draw.rect()
, получающая поверхность, цвет и объект Rect (левую и верхнюю координату, ширину и высоту) и рендерящая прямоугольник. Если дополнительный параметр ширины больше нуля, то он отрисовывает контур. Если ширина равна нулю (значение по умолчанию), то рисует сплошной прямоугольник.Стоит заметить, что класс
Brick
является подклассом GameObject
и получает все его свойства, но также имеет и цвет, который обрабатывает самостоятельно (потому что могут существовать игровые объекты, имеющие несколько цветов). Поле special_effect
мы пока рассматривать не будем.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)
Отрисовка мяча
Мяч в Breakout — это просто круг. В Pygame есть функция
pygame.draw.circle()
, получающая цвет, центр, радиус и дополнительный параметр ширины, который по умолчанию равен нулю. Как и в функции pygame.draw.rect()
, если ширина равна нулю, то отрисовывается сплошной круг. Ball тоже является подклассом GameObject.Так как мяч всегда движется (в отличие от кирпичей), он также имеет скорость, которая передаётся для обработки базовому классу
GameObject
. Класс Ball имеет небольшое отличие — параметры x и y обозначают его центр, а параметры x и y, передаваемые базовому классу GameObject
являются верхним левым углом ограничивающего прямоугольника. Чтобы преобразовать центр в верхний левый угол, достаточно вычесть радиус.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)
Отрисовка ракетки
Ракетка — это ещё один прямоугольник, двигающийся влево и вправо в ответ на нажатия игроком клавиш со стрелками. Это значит, что положение ракетки в разных кадрах может отличаться, но в процессе отрисовки это просто прямоугольник, который должен рендериться в текущей позиции, какой бы она ни была. Вот как выглядит соответствующий код:
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)
Заключение
В этой части мы узнали о классе TextObject и о том, как рендерить текст на экране. Также мы познакомились с тем, как рисовать объекты: кирпичи, мяч и ракетку.
В третьей части мы узнаем, как работает обработка событий и как Pygame позволяет нам перехватывать события и реагировать на них (нажатия клавиш, движение мыши и нажатия кнопок мыши). Также мы рассмотрим такие элементы игрового процесса, как движение мяча, задание его скорости и перемещение ракетки.