см. часть1
см. часть2
см. часть3
В этой части мы закончим игру Alien
Invasion. Мы добавим кнопку Play
для запуска игры по требованию или для перезапуска
или перезапустить игру после ее завершения. Мы также изменим игру
чтобы она ускорялась, когда игрок поднимается на уровень выше,
и внедрим систему подсчета очков.
- Добавление кнопки Play
- Создание класса кнопки
- Рисование кнопки на экране
- Запуск игры
- Сброс игры
- Отключение кнопки Play
- Скрытие курсора мыши
- Повышение уровня
- Изменение настроек скорости
- Сброс скорости
- Подсчет баллов
- Отображение счета
- Создание табло
- Обновление счета по мере уничтожения пришельцев
- Сброс очков
- Уверенность в том, что все удары засчитаны
- Увеличение количества очков
- Округление очков
- Высокие показатели
- Отображение уровня
- Отображение количества кораблей
Добавление кнопки Play
В этом разделе мы добавим кнопку Play, которая появляется перед началом игры
и снова появляется, когда игра заканчивается, чтобы игрок мог играть снова.
Сейчас игра начинается, как только вы запускаете файл alien_invasion.py. Давайте
запустим игру в неактивном состоянии, а затем предложим игроку нажать кнопку Play
чтобы начать игру. Для этого измените метод init() в GameStats:
#game_stats.py
def __init__(self, ai_game):
"""Initialize statistics."""
self.settings = ai_game.settings
self.reset_stats()
# Start game in an inactive state.
self.game_active = False
Теперь игра должна запускаться в неактивном состоянии без возможности для игрока
игрок не сможет запустить ее, пока мы не создадим кнопку Play.
Создание класса кнопки
Поскольку в Pygame нет встроенного метода для создания кнопок, мы
напишем класс Button для создания заполненного прямоугольника с надписью. Вот первая часть класса Button;
сохраните его как button.py:
# button.py
import pygame.font
class Button:
def __init__(self, ai_game, msg):
"""Initialize button attributes."""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
# Set the dimensions and properties of the button.
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)
# Build the button's rect object and center it.
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
# The button message needs to be prepped only once.
self._prep_msg(msg)
Сначала мы импортируем модуль pygame.font, который позволяет Pygame выводить текст
на экран. Метод init() принимает параметры self, объект ai_game
и msg, который содержит текст кнопки. Мы задаем размеры кнопки, затем устанавливаем button_color, чтобы окрасить объект button’s rect в ярко
зеленый и задаем text_color для отображения текста белым цветом.
Затем мы подготовим атрибут шрифта для отрисовки текста. Аргумент None
указывает Pygame на использование шрифта по умолчанию, а 48 задает размер текста. Чтобы
центрировать кнопку на экране, мы создаем прямоугольник для кнопки и устанавливаем его атрибут
center в соответствии с атрибутом экрана.
Pygame работает с текстом, отображая строку, которую вы хотите вывести на экран, как
изображение. Затем мы вызываем _prep_msg() для обработки этого рендеринга.
Вот код для _prep_msg():
#button.py
def _prep_msg(self, msg):
"""Turn msg into a rendered image and center text on the button."""
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 в изображение, которое мы затем сохраняем в self.msg_image. Метод font.render()
также принимает булево значение для включения или выключения сглаживания (сглаживание
делает края текста более плавными). Остальными аргументами являются
заданные цвет шрифта и цвет фона. Мы устанавливаем сглаживание в True
и установим фон текста того же цвета, что и кнопка. (Если вы не
включить цвет фона, Pygame попытается отобразить шрифт с переходящим
родительский фон.)
Затем мы центрируем изображение текста на кнопке, создав прямоугольник из
и установим его атрибут center в соответствии с атрибутом кнопки.
Наконец, мы создаем метод draw_button(), который можно вызвать для отображения кнопки на экране.
кнопку на экране:
#button.py
def draw_button(self):
# Draw blank button and then draw message.
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
Мы вызываем screen.fill(), чтобы нарисовать прямоугольную часть кнопки.
Затем мы вызываем screen.blit() для рисования текстового изображения на экране, передавая ему
изображение и объект rect, связанный с изображением. Это завершает работу класса
класс Button.
Рисование кнопки на экране
Мы будем использовать класс Button для создания кнопки Play в AlienInvasion. Сначала мы
обновим операторы импорта:
#alien_invasion.py
--snip--
from game_stats import GameStats
from button import Button
Поскольку нам нужна только одна кнопка Play, мы создадим ее в методе
init() метода AlienInvasion. Мы можем поместить этот код в самом конце
init():
#alien_invasion.py
def __init__(self):
--snip--
self._create_fleet()
# Make the Play button.
self.play_button = Button(self, "Play")
Этот код создает экземпляр кнопки Button с меткой Play, но он не
рисует кнопку на экране. Мы вызовем метод draw_button() кнопки
в _update_screen():
#alien_invasion.py
def _update_screen(self):
--snip--
self.aliens.draw(self.screen)
# Draw the play button if the game is inactive.
if not self.stats.game_active:
self.play_button.draw_button()
pygame.display.flip()
Чтобы сделать кнопку Play видимой поверх всех остальных элементов на экране,
мы рисуем ее после того, как все остальные элементы уже нарисованы, но перед переходом
на новый экран. Мы включаем ее в блок if, поэтому кнопка появляется только тогда.
когда игра неактивна.
Теперь, когда вы запустите игру Alien Invasion, вы должны увидеть кнопку Play в
в центре экрана.
Запуск игры
Чтобы начать новую игру, когда игрок нажимает кнопку Play, добавьте следующий блок elif
в конец блока _check_events(), чтобы отслеживать события мыши при нажатии на кнопку:
#alien_invasion.py
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
--snip--
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
Pygame обнаруживает событие MOUSEBUTTONDOWN, когда игрок щелкает в любом месте
на экране, но мы хотим ограничить нашу игру, чтобы она реагировала на щелчки мыши
только на кнопку Play. Для этого мы используем pygame.mouse.get_pos(),
который возвращает кортеж, содержащий координаты x- и y курсора мыши.
при нажатии на кнопку мыши. Мы отправляем эти значения в новый
метод _check_play_button().
Вот _check_play_button(), который я решил разместить после _check_events():
#alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
if self.play_button.rect.collidepoint(mouse_pos):
self.stats.game_active = True
Мы используем метод rect collidepoint() для проверки того, перекрывает ли точка
перекрывает ли точка щелчка мыши область, определенную прямоугольником кнопки Play. Если да, то мы
устанавливаем game_active в True, и игра начинается!
На этом этапе вы должны иметь возможность начать и играть в полноценную игру. Когда
игра закончится, значение game_active должно стать False, а кнопка Play
должна снова появиться кнопка Play.
Сброс игры
Код кнопки Play, который мы только что написали, срабатывает, когда игрок в первый раз нажимает кнопку
Play. Но он не работает после того, как первая игра закончится, потому что условия
которые привели к завершению игры, не были сброшены.
Чтобы перезапускать игру каждый раз, когда игрок нажимает кнопку Play, нам нужно сбросить
статистику игры, очистить старые пришельцы и пули, построить новый флот и
отцентрировать корабль, как показано здесь:
#alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
if self.play_button.rect.collidepoint(mouse_pos):
# Reset the game statistics.
self.stats.reset_stats()
self.stats.game_active = True
# Get rid of any remaining aliens and bullets.
self.aliens.empty()
self.bullets.empty()
# Create a new fleet and center the ship.
self._create_fleet()
self.ship.center_ship()
Мы сбрасываем статистику игры, что дает игроку три новых
корабля. Затем мы устанавливаем game_active в True, чтобы игра началась, как только
код в этой функции завершит выполнение. Мы опустошаем группы «Пришельцы» и «Пули
группы, а затем создаем новый флот и центрируем корабль.
Теперь игра будет правильно перезагружаться каждый раз, когда вы нажимаете кнопку Play, что позволит вам
играть в нее столько раз, сколько захотите!
Отключение кнопки Play
Одна из проблем с кнопкой Play заключается в том, что область кнопки на экране
продолжает реагировать на нажатия, даже когда кнопка Play не видна. Если вы
случайно нажать на область кнопки Play после начала игры, игра будет
перезапустится!
Чтобы исправить это, настройте игру так, чтобы она запускалась только тогда, когда game_active равно False:
#alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.stats.game_active:
# Reset the game statistics.
self.stats.reset_stats()
--snip--
Флаг button_clicked сохраняет значение True или False, и игра
будет перезапускаться только в том случае, если нажата кнопка Play и игра в данный момент не активна.
Чтобы проверить это поведение, начните новую игру и несколько раз щелкните на том месте, где должна находиться кнопка Play
должна быть кнопка Play. Если все работает так, как ожидается, щелчок по кнопке Play
не должно оказывать никакого влияния на игровой процесс.
Скрытие курсора мыши
Мы хотим, чтобы курсор мыши был виден для начала игры, но как только игра начинается, он
он просто мешает. Чтобы исправить это, мы сделаем его невидимым, когда игра становится
активной. Мы можем сделать это в конце блока if в _check_play_button():
#alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.stats.game_active:
--snip--
# Hide the mouse cursor.
pygame.mouse.set_visible(False)
Передача False в set_visible() говорит Pygame, что курсор будет скрыт, когда
когда мышь находится над игровым окном.
Мы заставим курсор снова появиться после окончания игры, чтобы игрок мог
снова нажать Play, чтобы начать новую игру. Вот код для этого:
#alien_invasion.py
def _ship_hit(self):
"""Respond to ship being hit by alien."""
if self.stats.ships_left > 0:
--snip--
else:
self.stats.game_active = False
pygame.mouse.set_visible(True)
Мы делаем курсор снова видимым, как только игра становится неактивной,
что происходит в _ship_hit(). Внимание к подобным деталям делает вашу игру
более профессиональной и позволяет игроку сосредоточиться на игре, а не на
чем разбираться с пользовательским интерфейсом.
Повышение уровня
В нашей текущей игре, когда игрок сбивает весь флот пришельцев, он достигает нового уровня.
игрок достигает нового уровня, но сложность игры не меняется. Давайте
немного оживим игру и сделаем ее более сложной, увеличив
скорость игры каждый раз, когда игрок очищает экран.
Изменение настроек скорости
Сначала мы реорганизуем класс Settings, чтобы сгруппировать настройки игры на
статические и изменяемые. Мы также убедимся, что настройки, изменяемые
во время игры, сбрасываются, когда мы начинаем новую игру. Вот метод init()
метод для файла settings.py:
#settings.py
def __init__(self):
"""Initialize the game's static settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
# Ship settings
self.ship_limit = 3
# Bullet settings
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
self.bullets_allowed = 3
# Alien settings
self.fleet_drop_speed = 10
# How quickly the game speeds up
self.speedup_scale = 1.1
self.initialize_dynamic_settings()
Мы продолжаем инициализировать те настройки, которые остаются неизменными, в методе init()
метод. Затем мы добавляем параметр speedup_scale для управления тем, как быстро игра
ускоряется: значение 2 удваивает скорость игры каждый раз, когда игрок
при достижении игроком нового уровня; при значении 1 скорость будет оставаться постоянной. Значение типа
1.1 должно увеличить скорость настолько, чтобы сделать игру сложной, но не
невозможной. Наконец, мы вызываем метод initialize_dynamic_settings(), чтобы инициализиро-
ния значений для атрибутов, которые должны меняться в течение игры.
Вот код для initialize_dynamic_settings():
#settings.py
def initialize_dynamic_settings(self):
"""Initialize settings that change throughout the game."""
self.ship_speed = 1.5
self.bullet_speed = 3.0
self.alien_speed = 1.0
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
Этот метод устанавливает начальные значения для скорости корабля, пули и пришельца.
скоростей. Мы будем увеличивать эти скорости по мере продвижения игрока в игре
и сбрасывать их каждый раз, когда игрок начинает новую игру. Мы включаем флот
_direction в этот метод, чтобы пришельцы всегда двигались вправо в начале
новой игры. Нам не нужно увеличивать значение параметра fleet_drop_speed,
потому что когда пришельцы будут двигаться быстрее по экрану, они также будут спускаться
вниз по экрану быстрее.
Чтобы увеличивать скорость корабля, пуль и пришельцев каждый раз.
когда игрок достигает нового уровня, мы напишем новый метод под названием
increase_speed():
#settings.py
def increase_speed(self):
"""Increase speed settings."""
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_speed *= self.speedup_scale
Чтобы увеличить скорость этих игровых элементов, мы умножим каждую скорость
на значение параметра speedup_scale.
Мы увеличиваем темп игры, вызывая функцию increase_speed() в _check
_bullet_alien_collisions(), когда последний пришелец во флоте был сбит:
#alien_invasion.py
def _check_bullet_alien_collisions(self):
--snip--
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()
Изменение значений параметров скорости корабля (ship_speed), скорости пришельца (alien_speed) и
bullet_speed достаточно, чтобы ускорить всю игру!
Сброс скорости
Теперь нам нужно возвращать все измененные настройки к их начальным значениям каждый
когда игрок начинает новую игру; иначе каждая новая игра будет начинаться
с увеличенными настройками скорости предыдущей игры:
#alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.stats.game_active:
# Reset the game settings.
self.settings.initialize_dynamic_settings()
--snip--
Теперь игра в Alien Invasion должна стать более увлекательной и сложной. Каждый
каждый раз, когда вы очищаете экран, игра ускоряется и становится немного
сложнее. Если игра становится слишком сложной слишком быстро, уменьшите значение параметра
значение параметра settings.speedup_scale. Или если игра недостаточно сложна,
немного увеличьте значение. Найдите оптимальный вариант, повышая сложность в течение
разумное количество времени. Первые несколько экранов должны быть легкими, следующие
следующие несколько — сложными, но выполнимыми, а последующие — почти до невозможности
сложными.
Подсчет баллов
Давайте реализуем систему подсчета очков, чтобы отслеживать счет в игре в реальном времени и
отображать высокий результат, уровень и количество оставшихся кораблей.
Счет является игровой статистикой, поэтому мы добавим атрибут счета в GameStats:
#game_stats.py
class GameStats:
--snip--
def reset_stats(self):
"""Initialize statistics that can change during the game."""
self.ships_left = self.ai_settings.ship_limit
self.score = 0
Чтобы сбрасывать счет при каждом запуске новой игры, мы инициализируем счет в функции
reset_stats(), а не init().
Отображение счета
Чтобы отобразить счет на экране, мы сначала создадим новый класс Scoreboard. Для
сейчас этот класс будет просто отображать текущий счет, но со временем мы будем использовать его для
его для отображения высокого результата, уровня и количества оставшихся кораблей.
Вот первая часть класса; сохраните ее как scoreboard.py:
#scoreboard.py
import pygame.font
class Scoreboard:
"""A class to report scoring information."""
def __init__(self, ai_game):
"""Initialize scorekeeping attributes."""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
self.settings = ai_game.settings
self.stats = ai_game.stats
# Font settings for scoring information.
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
# Prepare the initial score image.
self.prep_score()
Поскольку Scoreboard пишет текст на экран, мы начинаем с импорта модуля
pygame.font. Затем мы передаем init() параметр ai_game, чтобы он мог
получить доступ к объектам settings, screen и stats, которые понадобятся ему для отчета о
значения, которые мы отслеживаем. Затем мы задаем цвет текста и создаем объект шрифта
объект.
Чтобы превратить текст для отображения в изображение, мы вызываем prep_score(),
которую мы определяем здесь:
#scoreboard.py
def prep_score(self):
"""Turn the score into a rendered image."""
score_str = str(self.stats.score)
self.score_image = self.font.render(score_str, True,
self.text_color, self.settings.bg_color)
# Display the score at the top right of the screen.
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 20
self.score_rect.top = 20
В prep_score() мы превращаем числовое значение stats.score в строку,
а затем передаем эту строку в render(), которая создает изображение. Чтобы отобразить
счет четко отображается на экране, мы передаем цвет фона экрана и
цвет текста в render().
Мы расположим счет в правом верхнем углу экрана и
чтобы он расширялся влево по мере увеличения счета и ширины числа.
увеличивается. Для того чтобы счет всегда располагался в правом углу экрана, создадим прямоугольник под названием
экрана, мы создадим прямоугольник под названием score_rect и установим его правый край на расстоянии 20 пикселей
от правого края экрана. Затем мы разместим верхний край на расстоянии 20 пикселей
вниз от верхней границы экрана.
Затем мы создаем метод show_score(), чтобы отобразить визуализированный счет
изображение:
#scoreboard.py
def show_score(self):
"""Draw score to the screen."""
self.screen.blit(self.score_image, self.score_rect)
Этот метод рисует изображение счета на экране в месте, которое указывает score_rect
указывает.
Создание табло
Для отображения счета мы создадим экземпляр табло в AlienInvasion. Во-первых,
давайте обновим операторы импорта:
#alien_invasion.py
--snip--
from game_stats import GameStats
from scoreboard import Scoreboard
--snip--
Затем в init() мы создадим экземпляр Scoreboard:
#alien_invasion.py
def __init__(self):
--snip--
pygame.display.set_caption("Alien Invasion")
# Create an instance to store game statistics,
#and create a scoreboard.
self.stats = GameStats(self)
self.sb = Scoreboard(self)
--snip--
Затем мы рисуем табло на экране в _update_screen():
#alien_invasion.py
def _update_screen(self):
--snip--
self.aliens.draw(self.screen)
# Draw the score information.
self.sb.show_score()
# Draw the play button if the game is inactive.
--snip--
Мы вызываем show_score() непосредственно перед тем, как нарисовать кнопку Play.
Когда вы запустите игру Alien Invasion, в правом верхнем углу экрана должна появиться цифра 0.
экрана. (На данном этапе мы просто хотим убедиться, что счет появится в нужном месте, прежде чем разрабатывать систему подсчета очков.
правильном месте перед дальнейшей разработкой системы подсчета очков).
Далее мы присвоим очки каждому пришельцу!
Обновление счета по мере уничтожения пришельцев
Чтобы вывести на экран живой счет, мы обновляем значение stats.score всякий раз, когда
пришельца, а затем вызываем prep_score(), чтобы обновить изображение счета. Но
сначала давайте определим, сколько очков получает игрок каждый раз, когда он сбивает
сбивает инопланетянина:
#settings.py
def initialize_dynamic_settings(self):
--snip--
# Scoring
self.alien_points = 50
По мере прохождения игры мы будем увеличивать количество очков каждого пришельца. Для того чтобы
чтобы это значение очков сбрасывалось каждый раз, когда начинается новая игра, мы устанавливаем значение в функции
initialize_dynamic_settings().
Давайте обновлять счет каждый раз, когда инопланетянин сбит в _check_bullet
_alien_collisions():
#alien_invasion.py
def _check_bullet_alien_collisions(self):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(
self.bullets, self.aliens, True, True)
if collisions:
self.stats.score += self.settings.alien_points
self.sb.prep_score()
--snip--
Когда пуля попадает в инопланетянина, Pygame возвращает словарь столкновений.
Мы проверяем, существует ли этот словарь, и если да, то значение инопланетянина
добавляется к результату. Затем мы вызываем prep_score(), чтобы создать новое изображение для
обновленного результата.
Теперь, когда вы играете в Alien Invasion, вы должны иметь возможность набирать очки!
Сброс очков
Сейчас мы готовим новый счет только после поражения пришельца,
что работает на протяжении большей части игры. Но мы все еще видим старый счет, когда начинается новая
до тех пор, пока в новой игре не будет поражен первый пришелец.
Мы можем исправить эту ситуацию, добавляя счет при начале новой игры:
#alien_invasion.py
def _check_play_button(self, mouse_pos):
--snip--
if button_clicked and not self.stats.game_active:
--snip--
# Reset the game statistics.
self.stats.reset_stats()
self.stats.game_active = True
self.sb.prep_score()
--snip--
Мы вызываем prep_score() после сброса игровой статистики при начале новой игры.
игру. При этом на табло будет установлен счет 0.
Уверенность в том, что все удары засчитаны
В текущем виде наш код может не засчитывать очки для некоторых инопланетян. Например,
Например, если две пули столкнутся с инопланетянами во время одного и того же прохода через
или если мы сделаем сверхширокую пулю, чтобы попасть в нескольких пришельцев, игрок получит очки только в том случае, если попадет в нескольких пришельцев.
игрок получит очки только за попадание в одного из пришельцев. Чтобы исправить это,
давайте изменим способ обнаружения столкновений пули и пришельца.
В функции _check_bullet_alien_collisions() любая пуля, столкнувшаяся с пришельцем.
становится ключом в словаре столкновений. Значение, связанное с каждой
пулей, является список инопланетян, с которыми она столкнулась. Мы перебираем значения в
в словаре столкновений, чтобы убедиться, что мы начисляем очки за каждого сбитого пришельца:
#alien_invasion.py
def _check_bullet_alien_collisions(self):
--snip--
if collisions:
for aliens in collisions.values():
self.stats.score += self.settings.alien_points * len(aliens)
self.sb.prep_score()
--snip--
Если словарь столкновений был определен, мы перебираем все значения
в словаре. Помните, что каждое значение — это список инопланетян, пораженных одной пулей.
пулей. Мы умножаем значение каждого инопланетянина на количество инопланетян в каждом
и прибавляем эту сумму к текущему счету. Чтобы проверить это, измените ширину
пули до 300 пикселей и убедитесь, что вы получаете очки за каждого пришельца, которого вы
за каждого инопланетянина, в которого вы попали своими сверхширокими пулями; затем верните ширину пули к ее обычному
значение.
Увеличение количества очков
Поскольку игра становится сложнее каждый раз, когда игрок переходит на новый уровень,
инопланетяне на более поздних уровнях должны стоить больше очков. Для реализации этой функции
мы добавим код для увеличения стоимости очков, когда скорость игры
увеличивается:
settings.py
class Settings:
"""A class to store all settings for Alien Invasion."""
def __init__(self):
--snip--
# How quickly the game speeds up
self.speedup_scale = 1.1
# How quickly the alien point values increase
self.score_scale = 1.5
self.initialize_dynamic_settings()
def initialize_dynamic_settings(self):
--snip--
def increase_speed(self):
"""Increase speed settings and alien point values."""
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_speed *= self.speedup_scale
self.alien_points = int(self.alien_points * self.score_scale)
Мы определим скорость увеличения очков, которую назовем score_scale.
Небольшое увеличение скорости (1,1) быстро делает игру более сложной.
Но чтобы увидеть более заметную разницу в начислении очков, нам нужно изменить
значение очков пришельцев на большую величину (1,5). Теперь, когда мы увеличиваем скорость игры
скорость игры, мы также увеличиваем значение очков каждого попадания. Мы используем функцию int()
для увеличения значения очков на целые числа.
Чтобы увидеть значение каждого пришельца, добавьте вызов print() к методу increase_speed()
в Настройках:
#settings.py
def increase_speed(self):
--snip--
self.alien_points = int(self.alien_points * self.score_scale)
print(self.alien_points)
Новое значение очков должно появляться в терминале каждый раз, когда вы
достижении нового уровня.
Округление очков
В большинстве аркадных игр-стрелялок баллы выводятся в виде кратных 10, поэтому давайте
последуем этому примеру с нашими баллами. Кроме того, давайте отформатируем счет так.
запятые в больших числах. Это изменение мы сделаем в разделе Scoreboard:
#scoreboard.py
def prep_score(self):
"""Turn the score into a rendered image."""
rounded_score = round(self.stats.score, -1)
score_str = "{:,}".format(rounded_score)
self.score_image = self.font.render(score_str, True,
self.text_color, self.settings.bg_color)
--snip--
Функция round() обычно округляет десятичное число до заданного количества знаков после запятой, указанного в качестве второго аргумента. Однако, когда вы
передать отрицательное число в качестве второго аргумента, round() округлит значение
значение до ближайших 10, 100, 1000 и так далее. Приведенный выше код указывает Python
округлить значение stats.score до ближайших 10 и сохранить его в rounded_score.
Затем директива форматирования строки указывает Python вставить запятые в
при преобразовании числового значения в строку: например, чтобы
вывести 1,000,000 вместо 1000000. Теперь, когда вы запустите игру, вы должны
вы увидите аккуратно отформатированный округленный счет, даже когда наберете много очков.
Высокие показатели
Каждый игрок хочет побить максимальное количество очков в игре, поэтому давайте отслеживать и сообщать о высоком количестве очков.
чтобы игрокам было к чему стремиться. Мы будем хранить высокие баллы в
GameStats:
#game_stats.py
def __init__(self, ai_game):
--snip--
# High score should never be reset.
self.high_score = 0
Поскольку высокий результат никогда не должен сбрасываться, мы инициализируем high_score в функции
init(), а не в reset_stats().
Далее мы модифицируем Scoreboard для отображения высокого результата. Начнем с
метода init():
#scoreboard.py
def __init__(self, ai_game):
--snip--
# Prepare the initial score images.
self.prep_score()
self.prep_high_score()
Высокий счет будет отображаться отдельно от счета, поэтому нам нужен
новый метод, prep_high_score(), для подготовки изображения высокого результата.
Вот метод prep_high_score():
#scoreboard.py
def prep_high_score(self):
"""Turn the high score into a rendered image."""
high_score = round(self.stats.high_score, -1)
high_score_str = "{:,}".format(high_score)
self.high_score_image = self.font.render(high_score_str, True,
self.text_color, self.settings.bg_color)
# Center the high score at the top of the screen.
self.high_score_rect = self.high_score_image.get_rect()
self.high_score_rect.centerx = self.screen_rect.centerx
self.high_score_rect.top = self.score_rect.top
Мы округляем высокий балл до ближайших 10 и форматируем его запятыми.
Затем мы генерируем изображение из высокого результата, центрируем прямоугольник высокого результата
прямоугольник по горизонтали и устанавливаем его атрибут top так, чтобы он совпадал с верхом изображения с высоким результатом
изображение.
Теперь метод show_score() рисует текущий счет в правом верхнем углу
а высокий результат — в верхней центральной части экрана:
#scoreboard.py
def show_score(self):
"""Draw score to the screen."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
Для проверки высоких результатов мы напишем новый метод check_high_score(),
в Scoreboard:
#scoreboard.py
def check_high_score(self):
"""Check to see if there's a new high score."""
if self.stats.score > self.stats.high_score:
self.stats.high_score = self.stats.score
self.prep_high_score()
Метод check_high_score() проверяет текущий счет по сравнению с
высоким результатом. Если текущий результат больше, мы обновляем значение high_score
и вызываем prep_high_score() для обновления изображения высокого балла.
Нам нужно вызывать check_high_score() каждый раз, когда инопланетянин попадает в цель после обновления
счет в _check_bullet_alien_collisions():
#alien_invasion.py
def _check_bullet_alien_collisions(self):
--snip--
if collisions:
for aliens in collisions.values():
self.stats.score += self.settings.alien_points * len(aliens)
self.sb.prep_score()
self.sb.check_high_score()
--snip--
Мы вызываем check_high_score(), когда словарь столкновений присутствует, и
мы делаем это после обновления очков для всех пришельцев, которые были сбиты.
Когда вы играете в Alien Invasion в первый раз, ваш счет будет высоким,
поэтому он будет отображаться как текущий счет и высокий счет. Но когда вы
начнете вторую игру, ваш высокий счет должен отображаться посередине, а текущий счет — справа.
текущий счет — справа.
Отображение уровня
Чтобы отобразить уровень игрока в игре, нам сначала нужен атрибут в
GameStats, представляющий текущий уровень. Чтобы сбрасывать уровень в начале
каждой новой игры, инициализируйте его в функции reset_stats():
#game_stats.py
def reset_stats(self):
"""Initialize statistics that can change during the game."""
self.ships_left = self.settings.ship_limit
self.score = 0
self.level = 1
Чтобы Scoreboard отображал текущий уровень, мы вызываем новый метод prep
level() из __init_():
#scoreboard.py
def __init__(self, ai_game):
--snip--
self.prep_high_score()
self.prep_level()
Вот prep_level():
#scoreboard.py
def prep_level(self):
"""Turn the level into a rendered image."""
level_str = str(self.stats.level)
self.level_image = self.font.render(level_str, True,
self.text_color, self.settings.bg_color)
# Position the level below the score.
self.level_rect = self.level_image.get_rect()
self.level_rect.right = self.score_rect.right
self.level_rect.top = self.score_rect.bottom + 10
Метод prep_level() создает изображение из значения, хранящегося в файле
stats.level и устанавливает правый атрибут изображения в соответствие с правым атрибутом оценки
атрибуту. Затем он устанавливает верхний атрибут на 10 пикселей ниже нижней части
изображения результата, чтобы оставить пространство между результатом и уровнем.
Также необходимо обновить функцию show_score():
#scoreboard.py
def show_score(self):
"""Draw scores and level to the screen."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
Эта новая строка выводит изображение уровня на экран.
Мы увеличим stats.level и обновим изображение уровня в _check_bullet
_alien_collisions():
#alien_invasion.py
def _check_bullet_alien_collisions(self):
--snip--
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()
# Increase level.
self.stats.level += 1
self.sb.prep_level()
Если флот уничтожен, мы увеличиваем значение stats.level и вызываем функцию
prep_level(), чтобы убедиться, что новый уровень отображается правильно.
Чтобы изображение уровня правильно обновлялось в начале новой игры,
мы также вызываем prep_level(), когда игрок нажимает кнопку Play:
#alien_invasion.py
def _check_play_button(self, mouse_pos):
--snip--
if button_clicked and not self.stats.game_active:
--snip--
self.sb.prep_score()
self.sb.prep_level()
--snip--
Мы вызываем prep_level() сразу после вызова prep_score().
Теперь вы увидите, сколько уровней вы прошли.
Отображение количества кораблей
Наконец, давайте отобразим количество кораблей, оставшихся у игрока, но на этот раз
воспользуемся графикой. Для этого нарисуем корабли в верхнем левом углу
экрана, чтобы показать, сколько кораблей осталось, как во многих классических аркадных
игры.
Во-первых, нам нужно сделать Ship наследником от Sprite, чтобы мы могли создать группу
кораблей:
#ship.py
import pygame
from pygame.sprite import Sprite
class Ship(Sprite):
"""A class to manage the ship."""
def __init__(self, ai_game):
"""Initialize the ship and set its starting position."""
super().__init__()
--snip--
Здесь мы импортируем Sprite, убедимся, что Ship наследует от Sprite, и вызовем
super() в начале init().
Далее нам нужно изменить Scoreboard, чтобы создать группу кораблей, которые мы можем
отобразить. Вот утверждения импорта для Scoreboard:
#scoreboard.py
import pygame.font
from pygame.sprite import Group
from ship import Ship
Поскольку мы создаем группу кораблей, мы импортируем классы Group и Ship
классы.
Вот init():
#scoreboard.py
def __init__(self, ai_game):
"""Initialize scorekeeping attributes."""
self.ai_game = ai_game
self.screen = ai_game.screen
--snip--
self.prep_level()
self.prep_ships()
Мы присваиваем экземпляру игры атрибут, потому что он понадобится нам для
создания кораблей. Мы вызываем prep_ships() после вызова prep_level().
Вот prep_ships():
#scoreboard.py
def prep_ships(self):
"""Show how many ships are left."""
self.ships = Group()
for ship_number in range(self.stats.ships_left):
ship = Ship(self.ai_game)
ship.rect.x = 10 + ship_number * ship.rect.width
ship.rect.y = 10
self.ships.add(ship)
Метод prep_ships() создает пустую группу self.ships для хранения
экземпляров кораблей. Чтобы заполнить эту группу, цикл запускается один раз для каждого корабля, который
который покинул игрок. Внутри цикла мы создаем новый корабль и устанавливаем для каждого корабля значение
x-координату, чтобы корабли отображались рядом друг с другом с 10-пиксельным
с отступом в 10 пикселей от левой стороны группы кораблей. Мы устанавливаем значение координаты y
на 10 пикселей вниз от верхней части экрана, чтобы корабли появились в
левом верхнем углу экрана. Затем мы добавляем каждый новый корабль в группу
ships .
Теперь нам нужно нарисовать корабли на экране:
#scoreboard.py
def show_score(self):
"""Draw scores, level, and ships to the screen."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
self.ships.draw(self.screen)
Чтобы вывести корабли на экран, мы вызываем функцию draw() для группы, и
Pygame рисует каждый корабль.
Чтобы показать игроку, сколько кораблей у него есть для начала, мы вызываем функцию
prep_ships(), когда начинается новая игра. Мы делаем это в _check_play_button() в программе
AlienInvasion:
#alien_invasion.py
def _check_play_button(self, mouse_pos):
--snip--
if button_clicked and not self.stats.game_active:
--snip--
self.sb.prep_score()
self.sb.prep_level()
self.sb.prep_ships()
--snip--
Мы также вызываем prep_ships(), когда корабль подбит, чтобы обновить отображение кораблей
когда игрок теряет корабль:
#alien_invasion.py
def _ship_hit(self):
"""Respond to ship being hit by alien."""
if self.stats.ships_left > 0:
# Decrement ships_left, and update scoreboard.
self.stats.ships_left -= 1
self.sb.prep_ships()
--snip--
Мы вызываем prep_ships() после уменьшения значения ships_left, поэтому правильное количество кораблей отображается каждый раз, когда игрок теряет корабль.
правильное количество кораблей отображается каждый раз, когда корабль уничтожается.
Чтобы получить доступ к полному исходному коду проекта и файлам, посетите репозиторий на Github.
Вот и все для игры Alien Invasion. Далее я буду создавать игру flappy bird, используя pygame.