Построение игры начнется с создания пустого окна Pygame, в котором позднее будут отображаться игровые элементы — прежде всего корабль и пришельцы. Также игра должна реагировать на действия пользователя, назначать цвет фона и загружать изображение корабля.
Создание окна Pygame и обработка ввода
Начнем с создания пустого окна Pygame. Ниже приведена базовая структура игры, написанной с использованием Pygame:
alien_invasion.py
import sys
import pygame
def run_game():
. .# Инициализирует игру и создает объект экрана.
(1) . .pygame.init()
(2) . .screen = pygame.display.set_mode((1200, 800))
. .pygame.display.set_caption("Alien Invasion")
. .# Запуск основного цикла игры.
(3) . .while True:
. . . .# Отслеживание событий клавиатуры и мыши.
(4) . . . .for event in pygame.event.get():
(5) . . . . . .if event.type == pygame.QUIT:
. . . . . . . .sys.exit()
. . . . . . . .
. . . .# Отображение последнего прорисованного экрана.
? . . . .pygame.display.flip()
run_game()
Программа начинается с импортирования модуля sys и pygame. Модуль pygame содержит функциональность, необходимую для создания игры, а модуль sys завершает игру по команде игрока.
Игра Alien Invasion начинается с определения функции run_game(). Строка pygame.init() (1) инициализирует настройки, необходимые Pygame для нормальной работы. В точке (2) вызов pygame.display.set_mode() создает отображаемую область screen, на которой прорисовываются все графические элементы игры. Аргумент (1200, 800) представляет собой кортеж, определяющий размеры игрового окна. Передавая эти размеры pygame.display.set_mode(), мы создаем игровое окно с шириной 1200 пикселов и высотой 800 пикселов. (Вы можете изменить эти значения в соответствии с размерами своего монитора.)
Объект screen называется поверхностью (surface). Поверхность в Pygame представляет часть экрана, на которой отображается игровой элемент. Каждый элемент в игре (например, пришелец или корабль игрока) представлен поверхностью. Поверхность, возвращаемая display.set_mode(), представляет все игровое окно. При активизации игрового цикла анимации эта поверхность автоматически перерисовывается при каждом проходе цикла.
Процессом игры управляет цикл while (3), который содержит цикл событий и код, управляющий обновлениями экрана. Событием называется действие, выполняемое пользователем во время игры (например, нажатие клавиши или перемещение мыши). Чтобы наша программа реагировала на события, мы напишем цикл событий для прослушивания событий и выполнения соответствующей операции в зависимости от типа произошедшего события. Этим циклом событий является цикл for в точке (4).
Чтобы получить доступ к событиям, обнаруженным Pygame, мы используем метод pygame.event.get(). При любом событии клавиатуры или мыши отрабатывает цикл for. В этом цикле мы пишем серию команд if для обнаружения и обработки конкретных событий. Например, когда игрок щелкает на кнопке закрытия игрового окна, программа обнаруживает событие pygame.QUIT, и программа вызывает метод sys.exit() для выхода из игры (5).
Вызов pygame.display.flip() ? приказывает Pygame отобразить последний отрисованный экран. В данном случае при каждом выполнении цикла while будет отображаться пустой экран со стиранием старого экрана, так что виден будет только новый экран. При перемещении игровых элементов вызов pygame.display.flip() будет постоянно обновлять экран, отображая игровые элементы в новых позициях и скрывая старые изображения; таким образом создается иллюзия плавного движения.
Последняя строка в этой базовой структуре вызывает метод run_game(), который инициализирует игру и запускает основной цикл.
Запустите этот код, и вы увидите пустое окно Pygame.
Назначение цвета фона
Pygame по умолчанию создает черный экран, но это банально. Выберем другой цвет фона:
alien_invasion.py
...
def run_game():
...
pygame.display.set_caption("Alien Invasion")
. .# Назначение цвета фона.
(1) . .bg_color = (230, 230, 230)
# Запуск основного цикла игры.
while True:
# Отслеживание событий клавиатуры и мыши.
...
. . . .# При каждом проходе цикла перерисовывается экран.
(2) . . . .screen.fill(bg_color)
# Отображение последнего прорисованного экрана.
pygame.display.flip()
run_game()
Сначала программа создает цвет фона и сохраняет его в переменной bg_color (1) . Цвет достаточно задать только один раз, поэтому его значение определяется до входа в основной цикл while.
Цвета в Pygame задаются в схеме RGB: тройками интенсивности красной, зеленой и синей составляющих цвета. Значение каждой составляющей лежит в диапазоне от 0 до 255. Цветовое значение (255, 0, 0) соответствует красному цвету, (0, 255, 0) — зеленому и (0, 0, 255) — синему. Разные сочетания составляющих RGB позволяют создать до 16 миллионов цветов. В цветовом значении (230, 230, 230) красная, синяя и зеленая составляющие смешиваются в равных долях, давая светло-серый цвет фона.
В точке (2) экран заполняется цветом фона. Для этого вызывается метод screen.fill(), получающий всего один аргумент: цвет фона.
Создание класса Settings
Каждый раз, когда в нашу игру добавляется новая функциональность, также в нее обычно добавляются новые настройки (параметры конфигурации). Вместо того чтобы добавлять настройки в коде, мы напишем модуль с именем settings; этот модуль содержит класс с именем Settings для хранения всех настроек. Такое решение позволит передавать один объект вместо множества отдельных настроек. Кроме того, оно упрощает вызовы функций и упрощает изменение внешнего вида игры с ростом проекта. Чтобы внести изменения в игру, достаточно будет изменить некоторые значения в settings.py вместо того, чтобы искать разные настройки в файлах.
Исходная версия класса Settings выглядит так:
settings.py
class Settings():
. ."""Класс для хранения всех настроек игры Alien Invasion."""
. .def __init__(self):
. . . ."""Инициализирует настройки игры."""
. . . .# Параметры экрана
. . . .self.screen_width = 1200
. . . .self.screen_height = 800
. . . .self.bg_color = (230, 230, 230)
Чтобы создать экземпляр Settings и использовать его для обращения к настройкам, внесите изменения в alien_invasion.py:
alien_invasion.py
...
import pygame
from settings import Settings
def run_game():
. .# Инициализирует pygame, settings и объект экрана.
pygame.init()
(1) . .ai_settings = Settings()
(2) . .screen = pygame.display.set_mode(
. . . .(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# Запуск основного цикла игры.
while True:
...
# При каждом проходе цикла перерисовывается экран.
(3) . . . .screen.fill(ai_settings.bg_color)
. . . . . . . .
# Отображение последнего прорисованного экрана.
pygame.display.flip()
run_game()
Класс Settings импортируется в основной файл программы, после чего программа создает экземпляр Settings и сохраняет его в ai_settings после вызова pygame.init() (1) . При создании экрана (2) используются атрибуты screen_width и screen_height объекта ai_settings, после чего объект ai_settings также используется для получения цвета фона при заполнении экрана (3).
Добавление изображения корабля
А теперь добавим в игру космический корабль, которым управляет игрок. Чтобы вывести его на экран, мы загрузим изображение, после чего воспользуемся методом Pygame blit() для вывода изображения.
Выбирая графику для своих игр, обязательно обращайте внимание на условия лицензирования. Самый безопасный и дешевый начальный вариант — использование бесплатной графики с таких сайтов, как http://pixabay.com/.
В игре можно использовать практически любые графические форматы, но проще всего использовать файлы в формате .bmp, потому что этот формат Pygame загружает по умолчанию. И хотя Pygame можно настроить для других типов файлов, некоторые типы зависят от установки на компьютере определенных графических библиотек. (Большинство изображений, которые вы найдете, имеют формат .jpg, .png или .gif, но их можно преобразовать в формат .bmp при помощи таких программ, как Photoshop, GIMP или Paint.)
Обратите особое внимание на цвет фона вашего изображения. Попробуйте найти файл с прозрачным фоном, который можно заменить любым цветом фона в графическом редакторе. Чтобы ваша игра хорошо смотрелась, цвет фона изображения должен соответствовать цвету фона игры. Также можно подобрать цвет фона игры под цвет фона изображения.
В игре Alien Invasion используется файл ship.bmp (рис. 12.1), который можно загрузить в числе ресурсов книги по адресу https://www.nostarch.com/pythoncrashcourse/. Цвет фона файла соответствует настройкам, используемым в проекте. Создайте в главном каталоге проекта (alien_invasion) каталог с именем images. Сохраните файл ship.bmp в каталоге images.
Рис. 12.1. Корабль для игры Alien Invasion
Создание класса Ship
После того как изображение корабля выбрано, его необходимо вывести на экран. Для работы с кораблем мы напишем модуль ship, содержащий класс Ship. Этот класс реализует бульшую часть поведения корабля.
ship.py
import pygame
class Ship():
. .def __init__(self, screen):
. . . ."""Инициализирует корабль и задает его начальную позицию."""
. . . .self.screen = screen
. . . .# Загрузка изображения корабля и получение прямоугольника.
(1) . . . .self.image = pygame.image.load('images/ship.bmp')
(2) . . . .self.rect = self.image.get_rect()
(3) . . . .self.screen_rect = screen.get_rect()
. . . .# Каждый новый корабль появляется у нижнего края экрана.
(4) . . . .self.rect.centerx = self.screen_rect.centerx
. . . .self.rect.bottom = self.screen_rect.bottom
(5) . .def blitme(self):
. . . ."""Рисует корабль в текущей позиции."""
. . . .self.screen.blit(self.image, self.rect)
Сначала программа импортирует модуль pygame. Метод __init__() класса Ship получает два параметра: ссылку self и объект screen, на котором выводится корабль. Загрузка изображения выполняется вызовом pygame.image.load() (1) . Функция возвращает поверхность, представляющую корабль; полученный объект сохраняется в self.image.
После того как изображение будет загружено, метод get_rect() используется для получения атрибута rect поверхности (2). Один из факторов эффективности Pygame заключается в том, что программист может выполнять операции с игровыми элементами как с прямоугольниками даже в том случае, если они имеют другую форму. Операции с прямоугольниками эффективны, потому что прямоугольник — простая геометрическая фигура. Обычно этот подход работает достаточно хорошо и игроки не замечают, что программа не отслеживает точную геометрическую форму каждого игрового элемента.
При работе с объектом rect для вас доступны координаты x и y верхней, нижней, левой и правой сторон, а также центра. Присваивая любые из этих значений, вы задаете текущую позицию прямоугольника.
Местонахождение центра игрового элемента определяется атрибутами center, centerx или centery прямоугольника. Стороны определяются атрибутами top, bottom, left и right. Для изменения горизонтального или вертикального расположения прямоугольника достаточно задать атрибуты x и y, содержащие координаты левого верхнего угла. Эти атрибуты избавляют вас от вычислений, которые раньше разработчикам игр приходилось выполнять вручную, притом достаточно часто.
примечание
В Pygame начало координат (0, 0) находится в левом верхнем углу экрана, а оси направлены сверху вниз и слева направо. На экране с размерами 1200 на 800 начало координат располагается в левом верхнем углу, а правый нижний угол имеет координаты (1200, 800).
Корабль будет расположен в середине нижней стороны экрана. Для этого мы сначала сохраняем прямоугольник экрана в self.screen_rect (3), а затем присваиваем self.rect.centerx (координата x центра корабля) значение атрибута centerx прямоугольника экрана (4). Атрибуту self.rect.bottom (координата y низа корабля) присваивается значение атрибута bottom прямоугольника экрана. Pygame использует эти атрибуты rect для позиционирования изображения, чтобы корабль был выровнен по центру, а его нижний край совпадал с нижним краем экрана.
В точке (5) определяется метод blitme(), который выводит изображение на экран в позиции, заданной self.rect.
Вывод корабля на экран
Изменим программу alien_invasion.py, чтобы в ней создавался корабль (экземпляр Ship) и вызывался метод blitme() класса Ship:
alien_invasion.py
...
from settings import Settings
from ship import Ship
def run_game():
...
pygame.display.set_caption("Alien Invasion")
. .# Создание корабля.
(1) . .ship = Ship(screen)
# Start the main loop for the game.
while True:
...
# При каждом проходе цикла перерисовывается экран.
screen.fill(ai_settings.bg_color)
(2) . . . .ship.blitme()
. . . . . . . .
# Отображение последнего прорисованного экрана.
pygame.display.flip()
run_game()
После создания экрана программа импортирует класс Ship и создает его экземпляр (с именем ship). Это должно происходить до начала основного цикла while (1) , чтобы при каждом проходе цикла не создавался новый экземпляр корабля. Чтобы перерисовать корабль на экране, мы вызываем ship.blitme() после заполнения фона, так что корабль выводится поверх фона (2).
Если вы запустите alien_invasion.py сейчас, вы увидите пустой игровой экран, в центре нижней стороны которого находится корабль (рис. 12.2).
Рис. 12.2. Корабль в середине нижней стороны экрана