Статьи

Сборка игр с Python 3 и Pygame: часть 3

Это третья часть серии из пяти уроков, посвященных созданию игр с Python 3 и Pygame. Во второй части мы рассмотрели класс TextObject используемый для визуализации текста на экране, создали главное окно и научились рисовать такие объекты, как кубики, шарик и весло.

В этой части мы погрузимся в сердце Breakout и узнаем, как обрабатывать события, познакомимся с основным классом Breakout и посмотрим, как перемещать различные игровые объекты.

В Breakout есть три типа событий: события нажатия клавиш, события мыши и события таймера. Основной цикл в классе Game обрабатывает события нажатия клавиш и мыши и доставляет их подписчикам (вызывая функцию-обработчик).

Хотя класс Game очень универсален и не имеет специальных знаний для Breakout, сама подписка и способы обработки каждого события очень специфичны.

Класс Breakout — это то, где управляется большая часть знаний об игре Breakout. В этой серии мы встретимся с классом Breakout несколько раз. Вот строки, которые регистрируют различные обработчики событий.

Обратите внимание, что все события клавиш для левой и правой клавиш со стрелками будут идти к одному и тому же методу-обработчику весла.

1
2
3
4
5
6
7
8
# Register the handle_mouse_event() method of a button object
self.mouse_handlers.append(b.handle_mouse_event)
 
# Register the handle() method of the paddle to handle key events
self.keydown_handlers[pygame.K_LEFT].append(paddle.handle)
self.keydown_handlers[pygame.K_RIGHT].append(paddle.handle)
self.keyup_handlers[pygame.K_LEFT].append(paddle.handle)
self.keyup_handlers[pygame.K_RIGHT].append(paddle.handle)

Класс Game будет вызывать зарегистрированные обработчики для каждого ключевого события и передавать ключ. Обратите внимание, что это не класс Paddle. В Breakout единственным объектом, который заинтересован в этих событиях, является весло. Когда клавиша нажата или отпущена, вызывается метод handle() .

Paddle не нужно знать, было ли это событие нажатия клавиши key или up, потому что оно управляет текущим состоянием через пару логических переменных: moving_left и moving_right . Если moving_left равен True, это означает, что нажата левая клавиша со стрелкой, и следующее событие будет нажатием клавиши вверх, что отпустит его. То же самое касается правой клавиши со стрелкой. Логика так же проста, как переключение этих переменных в ответ на любое событие.

1
2
3
4
5
def handle(self, key):
       if key == pygame.K_LEFT:
           self.moving_left = not self.moving_left
       else:
           self.moving_right = not self.moving_right

В Breakout есть игровое меню, которое вы скоро встретите. Кнопка в меню обрабатывает различные события мыши, такие как движение и щелчки (события мыши и мыши). В ответ на эти события кнопка обновляет внутреннюю переменную состояния. Вот код обработки мыши:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def handle_mouse_event(self, type, pos):
       if type == pygame.MOUSEMOTION:
           self.handle_mouse_move(pos)
       elif type == pygame.MOUSEBUTTONDOWN:
           self.handle_mouse_down(pos)
       elif type == pygame.MOUSEBUTTONUP:
           self.handle_mouse_up(pos)
 
   def handle_mouse_move(self, pos):
       if self.bounds.collidepoint(pos):
           if self.state != ‘pressed’:
               self.state = ‘hover’
       else:
           self.state = ‘normal’
 
   def handle_mouse_down(self, pos):
       if self.bounds.collidepoint(pos):
           self.state = ‘pressed’
 
   def handle_mouse_up(self, pos):
       if self.state == ‘pressed’:
           self.on_click(self)
           self.state = ‘hover’

Обратите внимание, что метод handle_mouse_event() который зарегистрирован для получения событий мыши, проверяет тип события и передает его конкретному методу, который обрабатывает этот тип события.

События таймера не обрабатываются в основном цикле. Тем не менее, поскольку основной цикл вызывается каждый кадр, легко проверить, происходит ли определенное временное событие. Вы увидите пример этого позже при обсуждении временных эффектов.

Другая ситуация — когда мы хотим заморозить игру, например, при отображении сообщения, которое игрок должен прочитать без отвлечения. Метод show_message() класса Breakout использует этот подход и вызывает time.sleep() . Вот соответствующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import config as c
 
class Breakout(Game):
    def show_message(self,
                     text,
                     color=colors.WHITE,
                     font_name=’Arial’,
                     font_size=20,
                     centralized=False):
        message = TextObject(c.screen_width // 2,
                             c.screen_height // 2,
                             lambda: text, color,
                             font_name, font_size)
        self.draw()
        message.draw(self.surface, centralized)
        pygame.display.update()
        time.sleep(c.message_duration)

В части игрового процесса действуют правила Breakout (посмотрите, что я там делал?). Игровой процесс заключается в перемещении различных объектов в ответ на события и изменении состояния игры в зависимости от их взаимодействия.

Ранее вы видели, что класс Paddle реагирует на клавиши со стрелками, обновляя свои moving_left и moving_right . Фактическое движение происходит в методе update() . Здесь происходит некоторое вычисление, если весло находится близко к левому или правому краю экрана. Мы не хотим, чтобы манипулятор перемещался за пределы экрана (включая предопределенное смещение).

Таким образом, если движение привело бы его к краю, код настраивает движение так, чтобы оно остановилось точно на краю. Поскольку весло движется только горизонтально, вертикальная составляющая движения всегда равна нулю.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 update(self):
        if self.moving_left:
            dx = -(min(self.offset, self.left))
        elif self.moving_right:
            dx = min(self.offset, c.screen_width — self.right)
        else:
            return
 
        self.move(dx, 0)

Мяч просто использует функциональность базового класса GameObject , который перемещает игровые объекты в зависимости от их скорости (с горизонтальными и вертикальными компонентами). Скорость мяча определяется многими факторами в классе Breakout, которые вы скоро увидите. Поскольку перемещение — это просто добавление скорости к текущей позиции, направление движения шара полностью определяется его скоростью вдоль горизонтальной и вертикальной осей.

Мяч в Breakout появляется из ниоткуда в начале игры каждый раз, когда игрок теряет жизнь. Он просто материализуется из эфира и начинает падать либо прямо вниз, либо под небольшим углом. Когда шар создается в create_ball() , он получает скорость со случайным горизонтальным компонентом между -2 и 2 и вертикальным компонентом, который определяется в модуле config.py (в настоящее время установлен на 3).

1
2
3
4
5
6
7
8
def create_ball(self):
       speed = (random.randint(-2, 2), c.ball_speed)
       self.ball = Ball(c.screen_width // 2,
                        c.screen_height // 2,
                        c.ball_radius,
                        c.ball_color,
                        speed)
       self.objects.append(self.ball)

В этой части мы рассмотрели обработку событий, таких как нажатия клавиш, движение мыши и щелчки мыши. Мы также погрузились в некоторые элементы игрового процесса Breakout, такие как перемещение весла, перемещение мяча и контроль скорости мяча.

Не забудьте также посмотреть, что у нас есть в наличии для продажи и для изучения на рынке Envato, если вы хотите изучить больше материалов, связанных с Python.

В четвертой части мы рассмотрим важную тему обнаружения столкновений и посмотрим, что происходит, когда мяч попадает в различные игровые объекты, такие как весло, кирпичи и даже стены, потолок и пол.

Тогда мы обратим наше внимание на игровое меню. Мы создадим пользовательские кнопки, которые будем использовать в качестве меню, которое мы можем показывать и скрывать по мере необходимости.