Главная страница

Книга Изучаем Python


Скачать 4.68 Mb.
НазваниеКнига Изучаем Python
Дата10.12.2022
Размер4.68 Mb.
Формат файлаpdf
Имя файлаErik_Metiz_Izuchaem_Python_Programmirovanie_igr_vizualizatsia_da.pdf
ТипКнига
#837531
страница28 из 53
1   ...   24   25   26   27   28   29   30   31   ...   53
Рис. 12.1. Корабль для игры Alien Invasion

236 Глава 12 • Стреляющий корабль
Создание класса Ship
После того как изображение корабля выбрано, его необходимо вывести на экран.
Для работы с кораблем мы напишем модуль ship
, содержащий класс
Ship
. Этот класс реализует бульшую часть поведения корабля.
ship.py
import pygame class Ship():
def __init__(self, screen):
"""Инициализирует корабль и задает его начальную позицию."""
self.screen = screen
# Загрузка изображения корабля и получение прямоугольника.

self.image = pygame.image.load('images/ship.bmp')
 self.rect = self.image.get_rect()
 self.screen_rect = screen.get_rect()
# Каждый новый корабль появляется у нижнего края экрана.
 self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom
 def blitme(self):
"""Рисует корабль в текущей позиции."""
self.screen.blit(self.image, self.rect)
Сначала программа импортирует модуль pygame
. Метод
__init__()
класса
Ship по- лучает два параметра: ссылку self и объект screen
, на котором выводится корабль.
Загрузка изображения выполняется вызовом pygame.image.load()
. Функция возвращает поверхность, представляющую корабль; полученный объект сохраня- ется в self.image
После того как изображение будет загружено, метод get_rect()
используется для получения атрибута rect поверхности . Один из факторов эффективности
Pygame заключается в том, что программист может выполнять операции с игровы- ми элементами как с прямоугольниками даже в том случае, если они имеют другую форму. Операции с прямоугольниками эффективны, потому что прямоугольник — простая геометрическая фигура. Обычно этот подход работает достаточно хорошо и игроки не замечают, что программа не отслеживает точную геометрическую форму каждого игрового элемента.
При работе с объектом rect для вас доступны координаты x
и y
верхней, нижней, левой и правой сторон, а также центра. Присваивая любые из этих значений, вы задаете текущую позицию прямоугольника.
Местонахождение центра игрового элемента определяется атрибутами center
, centerx или centery прямоугольника. Стороны определяются атрибутами top
, bottom
, left и right
. Для изменения горизонтального или вертикального рас- положения прямоугольника достаточно задать атрибуты x
и y
, содержащие координаты левого верхнего угла. Эти атрибуты избавляют вас от вычислений, которые раньше разработчикам игр приходилось выполнять вручную, притом достаточно часто.

Добавление изображения корабля 237
ПРИМЕЧАНИЕ
В Pygame начало координат (0, 0) находится в левом верхнем углу экрана, а оси направлены сверху вниз и слева направо . На экране с размерами 1200 на 800 начало координат располагается в левом верхнем углу, а правый нижний угол имеет координаты (1200, 800) .
Корабль будет расположен в середине нижней стороны экрана. Для этого мы сна- чала сохраняем прямоугольник экрана в self.screen_rect
, а затем присваиваем self.rect.centerx
(координата x центра корабля) значение атрибута centerx пря- моугольника экрана . Атрибуту self.rect.bottom
(координата y низа корабля) присваивается значение атрибута bottom прямоугольника экрана. Pygame исполь- зует эти атрибуты rect для позиционирования изображения, чтобы корабль был выровнен по центру, а его нижний край совпадал с нижним краем экрана.
В точке  определяется метод 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")
# Создание корабля.
 ship = Ship(screen)
# Start the main loop for the game.
while True:
# При каждом проходе цикла перерисовывается экран.
screen.fill(ai_settings.bg_color)
 ship.blitme()
# Отображение последнего прорисованного экрана.
pygame.display.flip()
run_game()
После создания экрана программа импортирует класс
Ship и создает его экземпляр
(с именем ship
). Это должно происходить до начала основного цикла while
, чтобы при каждом проходе цикла не создавался новый экземпляр корабля. Чтобы перерисовать корабль на экране, мы вызываем ship.blitme()
после заполнения фона, так что корабль выводится поверх фона .
Если вы запустите alien_invasion .py сейчас, вы увидите пустой игровой экран, в цен- тре нижней стороны которого находится корабль (рис. 12.2).

238 Глава 12 • Стреляющий корабль
Рис. 12.2. Корабль в середине нижней стороны экрана
Рефакторинг: модуль game_functions
В больших проектах перед добавлением нового кода часто проводится рефакто- ринг уже написанного кода. Рефакторинг упрощает структуру существующего кода и дальнейшее развитие проекта. В этом разделе мы создадим новый модуль game_functions для хранения функций, обеспечивающих работу игры. Модуль game_functions предотвратит чрезмерное разрастание alien_invasion .py и сделает логику alien_invasion .py более простой и понятной.
Функция check_events()
Начнем с перемещения кода управления событиями в отдельную функцию check_
events()
. Тем самым вы упростите run_game()
и изолируете цикл управления событиями от остального кода. Изоляция цикла событий позволит организовать управление событиями отдельно от других аспектов игры (например, обновления экрана).
Поместим check_events()
в отдельный модуль с именем game_functions
:
game_functions.py
import sys import pygame def check_events():

Рефакторинг: модуль game_functions 239
"""Обрабатывает нажатия клавиш и события мыши."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
Этот модуль импортирует модули sys и pygame
, используемые в цикле обработки событий. На данный момент эта функция не получает параметров, а ее тело копи- руется из цикла событий в alien_invasion .py
Теперь изменим код alien_invasion .py
, чтобы он импортировал модуль game_
functions
, и мы заменим цикл событий вызовом check_events()
:
alien_invasion.py
import pygame from settings import Settings from ship import Ship import game_functions as gf def run_game():
# Запуск основного цикла игры.
while True:
gf.check_events()
# При каждом проходе цикла перерисовывается экран.
Импортировать модуль sys прямо в главный файл в программы уже не нужно, по- тому что он сейчас используется только в модуле game_functions
. Импортируемому модулю game_functions для удобства присваивается псевдоним gf
Функция update_screen()
Для дальнейшего упрощения run_game()
выделим код обновления экрана в от- дельную функцию update_screen()
в game_functions .py
:
game_functions.py
def check_events():
def update_screen(ai_settings, screen, ship):
"""Обновляет изображения на экране и отображает новый экран."""
# При каждом проходе цикла перерисовывается экран.
screen.fill(ai_settings.bg_color)
ship.blitme()
# Отображение последнего прорисованного экрана.
pygame.display.flip()
Новая функция update_screen()
получает три параметра: ai_settings
, screen и ship
. Теперь необходимо заменить цикл while из alien_invasion .py вызовом update_
sc reen()
:

240 Глава 12 • Стреляющий корабль
alien_invasion.py
# Запуск основного цикла игры.
while True:
gf.check_events()
gf.update_screen(ai_settings, screen, ship)
run_game()
Эти две функции упрощают цикл while и процесс дальнейшей разработки. Буль- шая часть работы будет выполняться не в run_game()
, а в модуле game_functions
Так как мы решили начать работу с кодом c одного файла, мы не стали вводить мо- дуль game_functions с самого начала. Эта последовательность дает представление о реальном процессе разработки: сначала вы пишете свой код в самом простом виде, а потом подвергаете его рефакторингу по мере роста сложности проекта.
Теперь, когда мы изменили структуру кода и упростили его расширение, можно переходить к динамическим аспектам игры!
УПРАЖНЕНИЯ
12-1 . Синее небо: создайте окно Pygame с синим фоном .
12-2 . Игровой персонаж: найдите изображение игрового персонажа, который вам нравится, в формате .bmp (или преобразуйте существующее изображение) . Создайте класс, который рисует персонажа в центре экрана, и приведите цвет фона изображения в соответствие с цветом фона экрана (или наоборот) .
Управление кораблем
Реализуем возможность перемещения корабля по горизонтали. Для этого мы на- пишем код, реагирующий на нажатие клавиш
или . Начнем с движения впра- во, а потом применим те же принципы к движению влево. Заодно вы научитесь управлять перемещением изображений на экране.
Обработка нажатия клавиши
Каждый раз, когда пользователь нажимает клавишу, это нажатие регистрируется в Pygame как событие. Каждое событие идентифицируется методом pygame.event.
get()
, поэтому в функции check_events()
необходимо указать, какие события должны отслеживаться. Каждое нажатие клавиши регистрируется как событие
KEYDOWN
При обнаружении события
KEYDOWN
необходимо проверить, была ли нажата кла- виша, инициирующая некоторое игровое событие. Например, при нажатии кла- виши
значение rect.centerx корабля увеличивается для перемещения корабля вправо:
game_functions.py
def check_events(ship):
"""Обрабатывает нажатия клавиш и события мыши."""

Управление кораблем 241
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
 elif event.type == pygame.KEYDOWN:
 if event.key == pygame.K_RIGHT:
# Переместить корабль вправо.
 ship.rect.centerx += 1
Функции check_events()
передается параметр ship
, потому что корабль должен двигаться вправо при нажатии клавиши
. Внутри check_events()
в цикл событий добавляется блок elif для выполнения кода при обнаружении события
KEYDOWN
.
Чтобы проверить, является ли нажатая клавиша клавишей
(
pygame.K_RIGHT
), мы читаем атрибут event.key
. Если нажата клавиша
, корабль перемещается вправо, для чего значение ship.rect.centerx увеличивается на 1 .
Вызов check_events()
в alien_invasion .py необходимо изменить, чтобы в аргументе передавался объект ship
:
alien_invasion.py
# Запуск основного цикла игры.
while True:
gf.check_events(ship)
gf.update_screen(ai_settings, screen, ship)
Если запустить программу alien_invasion .py сейчас, вы увидите, что корабль переме- щается вправо на 1 пиксел при каждом нажатии клавиши
. Неплохо для начала, но это не лучший способ управления кораблем. Чтобы управление было более удобным, следует реализовать возможность непрерывного перемещения.
Непрерывное перемещение
Если игрок удерживает клавишу
, корабль должен двигаться вправо до тех пор, пока клавиша не будет отпущена. Чтобы узнать, когда клавиша
будет отпущена, наша игра отслеживает событие pygame.KEYUP
; таким образом, реализация непре- рывного движения будет основана на отслеживании событий
KEYDOWN
и
KEYUP
в со- четании с флагом moving_right
В неподвижном состоянии корабля флаг moving_right равен
False
. При нажатии клавиши
флагу присваивается значение
True
, а когда клавиша будет отпущена, флаг возвращается в состояние
False
Класс
Ship управляет всеми атрибутами корабля, и мы добавим в него атрибут с именем moving_right и метод update()
для проверки состояния флага moving_
right
. Метод update()
изменяет позицию корабля, если флаг содержит значение
True
. Этот метод будет вызываться каждый раз, когда вы хотите обновить позицию корабля.
Ниже приведены изменения в классе
Ship
:
ship.py
class Ship():

242 Глава 12 • Стреляющий корабль def __init__(self, screen):
# Каждый новый корабль появляется у нижнего края экрана.
self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom
# Флаг перемещения
 self.moving_right = False
 def update(self):
"""Обновляет позицию корабля с учетом флага."""
if self.moving_right:
self.rect.centerx += 1
def blitme(self):
Мы добавляем атрибут self.moving_right в методе
__init__()
и инициализируем его значением
False
. Затем вызывается метод update()
, который перемещает корабль вправо, если флаг равен
True
.
Теперь внесем изменения в check_events()
, чтобы при нажатии клавиши
moving_
right присваивалось значение
True
, а при ее отпускании —
False
:
game_functions.py
def check_events(ship):
"""Обрабатывает нажатия клавиш и события мыши."""
for event in pygame.event.get():
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
 ship.moving_right = True
 elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
В точке  изменяется реакция игры при нажатии клавиши
; вместо непосред- ственного изменения позиции корабля программа просто присваивает moving_right значение
True
. В точке  добавляется новый блок elif
, реагирующий на события
KEYUP
. Когда игрок отпускает клавишу
(
K_RIGHT
), moving_right присваивается значение
False
Остается изменить цикл while в alien_invasion .py
, чтобы при каждом проходе цикла вызывался метод update()
корабля:
alien_invasion.py
# Запуск основного цикла игры.
while True:
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings, screen, ship)
Позиция корабля будет обновляться после проверки событий клавиатуры, но перед обновлением экрана. Таким образом, позиция корабля обновляется в от-

Управление кораблем 243
вет на действия пользователя и будет использоваться при перерисовке корабля на экране.
Если запустить alien_invasion .py и удерживать клавишу
, корабль непрерывно двигается вправо, пока клавиша не будет отпущена.
Перемещение влево и вправо
Теперь, когда мы реализовали непрерывное движение вправо, добавить движе- ние влево относительно несложно. Для этого нужно снова изменить класс
Ship и функцию check_events()
. Ниже приведены необходимые изменения в
__init__()
и update()
в классе
Ship
:
ship.py
def __init__(self, screen):
# Флаги перемещения self.moving_right = False self.moving_left = False def update(self):
"""Обновляет позицию корабля с учетом флагов."""
if self.moving_right:
self.rect.centerx += 1
if self.moving_left:
self.rect.centerx -= 1
В методе
__init__()
добавляется флаг self.moving_left
. В update()
используются два отдельных блока if вместо elif
, чтобы при нажатии обеих клавиш со стрелками атрибут rect.centerx сначала увеличивался, а потом уменьшался. В результате корабль остается на месте. Если бы для движения влево использовался блок elif
, то клавиша
всегда имела бы приоритет. Такая реализация повышает точность перемещения при переключении направления, когда игрок может ненадолго удер- живать нажатыми обе клавиши.
В check_events()
необходимо внести два изменения:
game_functions.py
def check_events(ship):
"""Обрабатывает нажатия клавиш и события мыши."""
for event in pygame.event.get():
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
ship.moving_right = True elif event.key == pygame.K_LEFT:
ship.moving_left = True elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False elif event.key == pygame.K_LEFT:
ship.moving_left = False

244 Глава 12 • Стреляющий корабль
Если событие
KEYDOWN
происходит для события
K_LEFT
, то moving_left присваи- вается
True
. Если событие
KEYUP
происходит для события
K_LEFT
, то moving_left присваивается
False
. Здесь возможно использовать блоки elif
, потому что каждое событие связано только с одной клавишей. Если же игрок нажимает обе клавиши одновременно, то программа обнаруживает два разных события.
Если вы запустите alien_invasion .py
, то увидите, что корабль может непрерывно двигаться влево и вправо. Если же нажать обе клавиши, корабль останавливается.
Следующий шаг — доработка движения корабля. Внесем изменения в скорость и ограничим величину перемещения, чтобы корабль не выходил за края экрана.
Регулировка скорости корабля
В настоящий момент корабль смещается на один пиксел за каждый проход цикла while
, но для повышения точности управления скоростью можно добавить в класс
Settings атрибут ship_speed_factor
. Этот атрибут определяет величину смещения корабля при каждом проходе цикла. Новый атрибут settings .py выглядит так:
settings.py
class Settings():
"""Класс для хранения всех настроек игры Alien Invasion."""
def __init__(self):
# Настройки корабля
Переменной ship_speed_factor присваивается значение 1.5. При перемещении ко- рабля его позиция изменяется на 1,5 пиксела вместо 1. Дробные значения скорости позволят лучше управлять скоростью корабля при последующем повышении темпа игры. Однако атрибуты прямоугольников (такие, как centerx
) принимают только целочисленные значения, поэтому в
Ship необходимо внести ряд изменений:
ship.py
class Ship():
 def __init__(self, ai_settings, screen):
"""Инициализирует корабль и задает его начальную позицию."""
self.screen = screen
 self.ai_settings = ai_settings
# Каждый новый корабль появляется у нижнего края экрана
# Сохранение вещественной координаты центра корабля.
 self.center = float(self.rect.centerx)
# Флаги перемещения self.moving_right = False self.moving_left = False

Управление кораблем
1   ...   24   25   26   27   28   29   30   31   ...   53


написать администратору сайта