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

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


Скачать 4.68 Mb.
НазваниеКнига Изучаем Python
Дата10.12.2022
Размер4.68 Mb.
Формат файлаpdf
Имя файлаErik_Metiz_Izuchaem_Python_Programmirovanie_igr_vizualizatsia_da.pdf
ТипКнига
#837531
страница32 из 53
1   ...   28   29   30   31   32   33   34   35   ...   53
274 Глава 13 • Осторожно, пришельцы!
self.bullet_speed_factor = 3
self.bul
Оптимальное значение этой настройки зависит от производительности вашей системы. Найдите значение, которое лучше подходит для вашей конкретной кон- фигурации.
Рефакторинг update_bullets()
Переработаем функцию update_bullets()
, чтобы она не решала такое количество разных задач. Код обработки коллизий будет выделен в отдельную функцию:
game_functions.py
def update_bullets(ai_settings, screen, ship, aliens, bullets):
# Уничтожение исчезнувших пуль.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)
def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
"""Обработка коллизий пуль с пришельцами."""
# Удаление пуль и пришельцев, участвующих в коллизиях.
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if len(aliens) == 0:
# Уничтожение существующих пуль и создание нового флота.
bullets.empty()
create_fleet(ai_settings, screen, ship, aliens)
Мы создали новую функцию check_bullet_alien_collisions()
для выявления коллизий между пулями и пришельцами и для реакции на уничтожение всего флота. Это сделано для того, чтобы сократить длину функции update_bullets()
и упростить дальнейшую разработку.
УПРАЖНЕНИЯ
13-5 . Ловец: создайте игру с персонажем, который может двигаться влево и вправо у ниж- него края экрана . Мяч появляется в случайной позиции у верхнего края и падает вниз с постоянной скоростью . Если персонаж «ловит» мяч, сталкиваясь с ним, мяч исчезает .
Создавайте новый мяч каждый раз, когда персонаж ловит мяч или когда мяч исчезает у нижнего края экрана .
Завершение игры
Какое удовольствие от игры, в которой невозможно проиграть? Если игрок не успеет сбить флот достаточно быстро, пришельцы уничтожат корабль при столкновении. При этом количество кораблей, используемых игроком, ограничено, и корабль уничтожается, когда пришелец достигает нижнего края экрана. Игра завершается в тот момент, когда у игрока кончатся все корабли.

Завершение игры 275
Обнаружение коллизий с кораблем
Начнем с проверки коллизий между пришельцами и кораблем, чтобы мы могли правильно обработать столкновения с пришельцами. Коллизии «при- шелец-корабль» проверяются немедленно после обновления позиции каждого пришельца:
game_functions.py
def update_aliens(ai_settings, ship, aliens):
"""
Проверяет, достиг ли флот края экрана,
после чего обновляет позиции всех пришельцев во флоте.
"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
# Проверка коллизий "пришелец-корабль".
 if pygame.sprite.spritecollideany(ship, aliens):
 print("Ship hit!!!")
Метод spritecollideany()
получает два аргумента: спрайт и группу. Метод пыта- ется найти любой элемент группы, вступивший в коллизию со спрайтом, и оста- навливает цикл по группе сразу же после обнаружения столкнувшегося элемента.
В данном случае он перебирает группу aliens и возвращает первого пришельца, столкнувшегося с кораблем.
Если ни одна коллизия не обнаружена, spritecollideany()
возвращает
None
, и блок if в точке  не выполняется. Если же будет обнаружен пришелец, столкнувшийся с кораблем, метод возвращает этого пришельца, и выполняется блок if
: выводится сообщение
Ship hit!!!
. (При столкновении пришельца с кораблем необходимо выполнить ряд операций: удалить всех оставшихся пришельцев и пули, вернуть корабль в центр и создать новый флот. Прежде чем писать код всех этих операций, необходимо убедиться в том, что решение с обнаружением коллизий с кораблем работает правильно. Команда print всего лишь позволяет легко проверить правиль- ность обнаружения коллизий.)
Далее необходимо передать ship функции update_aliens()
:
alien_invasion.py
# Запуск основного цикла игры.
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
gf.update_aliens(ai_settings, ship, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
Если вы запустите Alien Invasion, при столкновении пришельца с кораблем в тер- минальном окне появляется сообщение
Ship hit!!!
. В ходе тестирования этого аспекта присвойте alien_drop_speed более высокое значение (например, 50 или
100), чтобы пришельцы быстрее добирались до вашего корабля.

276 Глава 13 • Осторожно, пришельцы!
Обработка столкновений с кораблем
Теперь нужно разобраться, что же происходит при столкновении пришельца с кораблем. Вместо того чтобы уничтожать экземпляр ship и создавать новый, мы будем подсчитывать количество уничтоженных кораблей; для этого следует организовать сбор статистики по игре. (Статистика также пригодится для под- счета очков.)
Напишем новый класс
GameStats для ведения статистики и сохраним его в файле game_stats .py
:
game_stats.py
class GameStats():
"""Отслеживание статистики для игры Alien Invasion."""
def __init__(self, ai_settings):
"""Инициализирует статистику."""
self.ai_settings = ai_settings
 self.reset_stats()
def reset_stats(self):
"""Инициализирует статистику, изменяющуюся в ходе игры."""
self.ships_left = self.ai_settings.ship_limit
На все время работы Alien Invasion будет создаваться один экземпляр
GameStats
, но часть статистики должна сбрасываться в начале каждой новой игры. Для этого бульшая часть статистики будет инициализироваться в методе reset_stats()
вместо
__init__()
. Этот метод будет вызываться из
__init__()
, чтобы статистика правильно инициализировалась при первом создании экземпляра
GameStats
, а метод reset_stats()
будет вызываться в начале каждой новой игры.
Пока в игре используется всего один вид статистики — значение ships_left
, из- меняющееся в ходе игры. Количество кораблей в начале игры хранится в settings .py под именем ship_limit
:
settings.py
# Настройки корабля self.ship_speed_factor = 1.5
self.ship_limit = 3
Также необходимо внести ряд изменений в alien_invasion .py для создания экзем- пляра
GameStats
:
alien_invasion.py
from settings import Settings
 from game_stats import GameStats def run_game():
pygame.display.set_caption("Alien Invasion")

Завершение игры 277
# Создание экземпляра для хранения игровой статистики.
 stats = GameStats(ai_settings)
# Запуск основного цикла игры.
while True:
gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
 gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)
Мы импортируем новый класс
GameStats
, создаем экземпляр stats
, а затем добавляем аргументы stats
, screen и ship в вызов update_aliens()
. Эти аргу- менты будут использоваться для отслеживания количества кораблей, оставшихся у игрока, и построения нового флота при столкновении пришельца с кораблем.
Когда пришелец сталкивается с кораблем, программа уменьшает количество оставшихся кораблей на 1, уничтожает всех существующих пришельцев и пули, создает новый флот и возвращает корабль в середину экрана. (Также игра нена- долго приостанавливается, чтобы игрок заметил столкновение и перестроился перед появлением нового флота.)
Бульшая часть этого кода будет включена в функцию ship_hit()
:
game_functions.py
import sys
 from time import sleep import pygame def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
"""Обрабатывает столкновение корабля с пришельцем."""
# Уменьшение ships_left.
 stats.ships_left -= 1
# Очистка списков пришельцев и пуль.
 aliens.empty()
bullets.empty()
# Создание нового флота и размещение корабля в центре.
 create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
# Пауза.
 sleep(0.5)
 def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
# Проверка коллизий "пришелец-корабль".
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
Сначала мы импортируем функцию sleep()
из модуля time
, чтобы приостановить игру . Новая функция ship_hit()
управляет реакцией игры на столкновение

278 Глава 13 • Осторожно, пришельцы!
корабля с пришельцем. Внутри ship_hit()
число оставшихся кораблей уменьша- ется на 1 , после чего происходит очистка групп aliens и bullets
.
Затем программа создает новый флот и выравнивает корабль по центру нижнего края . (Вскоре мы добавим метод center_ship()
в класс
Ship
.) Наконец, после внесения изменений во все игровые элементы, но до перерисовки изменений на экране делается короткая пауза, чтобы игрок увидел, что его корабль столкнул- ся с пришельцем . После завершения паузы sleep()
код переходит к функции update_screen()
, которая перерисовывает новый флот на экране.
Также необходимо обновить определение update_aliens()
и добавить параметры stats
, screen и bullets

, чтобы эти значения можно было передать при вызове ship_hit()
Ниже приведен новый метод center_ship()
; добавьте его в конец ship .py
:
ship.py
def center_ship(self):
"""Размещает корабль в центре нижней стороны."""
self.center = self.screen_rect.centerx
Чтобы выровнять корабль по центру, мы задаем атрибуту center корабля значение, соответствующее центру экрана (полученное при помощи атрибута screen_rect
).
ПРИМЕЧАНИЕ
Обратите внимание: программа никогда не создает более одного корабля . Один экземпляр ship ис- пользуется на протяжении всей игры, а при столкновении с пришельцем он просто возвращается к центру экрана . О том, что у игрока не осталось ни одного корабля, программа узнаёт из атрибута ships_left .
Запустите игру, подстрелите нескольких пришельцев, а затем позвольте пришельцу столкнуться с кораблем. Происходит небольшая пауза, на экране появляется новый флот вторжения, а корабль возвращается в центр нижней части экрана.
Достижение нижнего края экрана
Если пришелец добирается до нижнего края экрана, программа будет реагировать так же, как при столкновении с кораблем. Добавьте для проверки этого условия новую функцию, которая будет называться update_aliens()
:
game_functions.py
def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
"""Проверяет, добрались ли пришельцы до нижнего края экрана."""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
 if alien.rect.bottom >= screen_rect.bottom:
# Происходит то же, что при столкновении с кораблем.
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
break def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):

Завершение игры 279
# Проверка пришельцев, добравшихся до нижнего края экрана.
 check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)
Функция check_aliens_bottom()
проверяет, есть ли хотя бы один пришелец, до- бравшийся до нижнего края экрана. Условие выполняется, когда атрибут rect.
bottom пришельца больше или равен атрибуту rect.bottom экрана . Если при- шелец добрался до низа, вызывается функция ship_hit()
. Если хотя бы один пришелец добрался до нижнего края, проверять остальных уже не нужно, поэтому после вызова ship_hit()
цикл прерывается.
Функция check_aliens_bottom()
вызывается после обновления позиций всех пришельцев и после проверки столкновений «пришелец-корабль» . Теперь новый флот будет появляться как при столкновении корабля с пришельцем, так и в том случае, если кто-то из пришельцев смог добраться до нижнего края экрана.
Конец игры
Программа Alien Invasion уже на что-то похожа, но игра длится бесконечно. Зна- чение ships_left просто продолжает уходить в отрицательную бесконечность.
Добавим в
GameStats новый атрибут — флаг game_active
, который завершает игру после потери последнего корабля:
game_stats.py
def __init__(self, settings):
# Игра Alien Invasion запускается в активном состоянии.
self.game_active = True
Добавим в ship_hit()
код, который сбрасывает флаг game_active в состояние
False при потере игроком последнего корабля:
game_functions.py
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
"""Обрабатывает столкновение корабля с пришельцем."""
if stats.ships_left > 0:
# Уменьшение ships_left.
stats.ships_left -= 1
# Пауза.
sleep(0.5)
else:
stats.game_active = False
Бульшая часть кода ship_hit()
осталась неизменной. Весь существующий код был перемещен в блок if
, который проверяет, что у игрока остался хотя бы один корабль. Если корабли не кончились, программа создает новый флот, делает паузу и продолжает игру. Если же игрок потерял последний корабль, флаг game_active переводится в состояние
False

280 Глава 13 • Осторожно, пришельцы!
Определение исполняемых частей игры
В файле alien_invasion .py необходимо определить части игры, которые должны вы- полняться всегда, и те части, которые должны выполняться только при активной игре:
alien_invasion.py
# Запуск основного цикла игры.
while True:
gf.check_events(ai_settings, screen, ship, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
gf.update_aliens(ai_settings, ship, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
В основном цикле всегда должна вызываться функция check_events()
, даже если игра находится в неактивном состоянии. Например, программа все равно долж- на узнать о том, что пользователь нажал клавишу Q для завершения игры или щелкнул на кнопке закрытия окна. Также экран должен обновляться в то время, пока игрок решает, хочет ли он начать новую игру. Остальные вызовы функций должны происходить только при активной игре, потому что в то время, когда игра не активна, обновлять позиции игровых элементов не нужно.
В обновленной версии игра должна останавливаться после потери игроком по- следнего корабля.
УПРАЖНЕНИЯ
13-6 . Конец игры: в коде из упражнения 13-5 (с . 274) подсчитывайте, сколько раз игрок не поймал мяч . После трех промахов игра должна заканчиваться .
Итоги
В этой главе вы научились добавлять в игру большое количество одинаковых элементов на примере флота пришельцев. Вы узнали, как использовать вложен- ные циклы для создания сетки с элементами, а также привели игровые элементы в движение, вызывая метод update()
каждого элемента. Вы научились управлять перемещением объектов на экране и обрабатывать различные события (например, достижение края экрана). Вы также узнали, как обнаруживать коллизии и реаги- ровать на них (на примере попаданий пуль в пришельцев и столкновений при- шельцев с кораблем). В завершение главы рассматривалась тема ведения игровой статистики и использования флага для проверки окончания игры.
В последней главе этого проекта будет добавлена кнопка
Play
, чтобы игрок мог самостоятельно запустить свою первую игру, а также повторить игру после ее за- вершения. После каждого уничтожения вражеского флота скорость игры будет возрастать, а мы реализуем систему подсчета очков. В результате вы получите полностью работоспособную игру!

14
Ведение счета
В этой главе построение игры Alien Invasion будет завершено. Мы добавим кнопку
Play для запуска игры по желанию игрока или перезапуска игры после ее завершения. Мы также изменим игру, чтобы она ускорялась при переходе игрока на следующий уровень, и реализуем систему подсчета очков. К концу главы вы будете знать достаточно, чтобы заняться разработкой игр, сложность которых нарастает по ходу игры и в которых реализована система подсчета очков.
Добавление кнопки Play
В этом разделе мы добавим кнопку
Play
, которая отображается перед началом игры и появляется после ее завершения, чтобы игрок мог сыграть снова.
В текущей версии игра начинается сразу же после запуска alien_invasion .py
. После очередных изменений игра будет запускаться в неактивном состоянии и предла- гать игроку нажать кнопку
Play для запуска. Для этого включите следующий код в game_stats .py
:
game_stats.py
def __init__(self, ai_settings):
"""Инициализирует статистику."""
self.ai_settings = ai_settings self.reset_stats()
# Игра запускается в неактивном состоянии.
self.game_active = False def reset_stats(self):
Итак, программа запускается в неактивном состоянии, а игру можно запустить только нажатием кнопки
Play
Создание класса Button
Так как в Pygame не существует встроенного метода создания кнопок, мы напишем класс
Button для создания заполненного прямоугольника с текстовой надписью.

282 Глава 14 • Ведение счета
Следующий код может использоваться для создания кнопок в любой игре. Ниже приведена первая часть класса
Button
; сохраните ее в файле button .py
:
button.py
import pygame.font class Button():
 def __init__(self, ai_settings, screen, msg):
"""Инициализирует атрибуты кнопки."""
self.screen = screen self.screen_rect = screen.get_rect()
# Назначение размеров и свойств кнопок.
 self.width, self.height = 200, 50
self.button_color = (0, 255, 0)
self.text_color = (255, 255, 255)
 self.font = pygame.font.SysFont(None, 48)
# Построение объекта rect кнопки и выравнивание по центру экрана.
 self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
# Сообщение кнопки создается только один раз.
 self.prep_msg(msg)
Сначала программа импортирует модуль pygame .font
, который позволяет Pygame выводить текст на экран. Метод
__init__()
получает параметры self
, объекты ai_settings и screen
, а также строку msg с текстом кнопки . Размеры кнопки задаются в точке , после чего атрибуты button_color и text_color задаются так, чтобы прямоугольник кнопки был окрашен в ярко-зеленый цвет, а текст выводился белым цветом.
В точке  происходит подготовка атрибута font для вывода текста. Аргумент
None сообщает Pygame, что для вывода текста должен использоваться шрифт по умолча- нию, а значение 48 определяет размер текста. Чтобы выровнять кнопку по центру экрана, мы создаем объект rect для кнопки  и задаем его атрибут center в соот- ветствии с одноименным атрибутом экрана.
Pygame выводит строку текста в виде графического изображения. В точке  эта задача решается методом prep_msg()
. Код prep_msg()
выглядит так:
button.py
def prep_msg(self, msg):
"""Преобразует msg в прямоугольник и выравнивает текст по центру."""
 self.msg_image = self.font.render(msg, True, self.text_color,
self.button_color)
 self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
Метод prep_msg()
должен получать параметр self и текст, который нужно вы- вести в графическом виде (
msg
). Вызов font.render()
преобразует текст, храня- щийся в msg
, в изображение, которое затем сохраняется в msg_image
. Методу

Добавление кнопки Play
1   ...   28   29   30   31   32   33   34   35   ...   53


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