Part 13

События

До сих пор наши главные циклы выполняли только заранее заданные анимации и реагировали только на события типа pygame.QUIT, хотя цикл получает список всех событий от операционной системы. Теперь разберемся и с другими типами событий.

Обработка событий

Эта программа выводит информацию обо всех событиях, которые операционная система передает программе pygame во время ее работы:

import pygame

pygame.init()
window = pygame.display.set_mode((640, 480))

while True:
    for event in pygame.event.get():
        print(event)
        if event.type == pygame.QUIT:
            exit()

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

<Event(4-MouseMotion {'pos': (495, 274), 'rel': (495, 274), 'buttons': (0, 0, 0), 'window': None})>
<Event(4-MouseMotion {'pos': (494, 274), 'rel': (-1, 0), 'buttons': (0, 0, 0), 'window': None})>
<Event(4-MouseMotion {'pos': (492, 274), 'rel': (-2, 0), 'buttons': (0, 0, 0), 'window': None})>
<Event(4-MouseMotion {'pos': (491, 274), 'rel': (-1, 0), 'buttons': (0, 0, 0), 'window': None})>
<Event(5-MouseButtonDown {'pos': (491, 274), 'button': 1, 'window': None})>
<Event(6-MouseButtonUp {'pos': (491, 274), 'button': 1, 'window': None})>
<Event(2-KeyDown {'unicode': 'a', 'key': 97, 'mod': 0, 'scancode': 38, 'window': None})>
<Event(3-KeyUp {'key': 97, 'mod': 0, 'scancode': 38, 'window': None})>
<Event(2-KeyDown {'unicode': 'b', 'key': 98, 'mod': 0, 'scancode': 56, 'window': None})>
<Event(3-KeyUp {'key': 98, 'mod': 0, 'scancode': 56, 'window': None})>
<Event(2-KeyDown {'unicode': 'c', 'key': 99, 'mod': 0, 'scancode': 54, 'window': None})>
<Event(3-KeyUp {'key': 99, 'mod': 0, 'scancode': 54, 'window': None})>
<Event(12-Quit {})>

Первые несколько событий связаны с использованием мыши, затем идут события от клавиатуры, и наконец последнее событие закрывает программу. У каждого события есть как минимум тип, но они также могут содержать и другую идентифицирующую информацию, например положение курсора мыши или нажатую клавишу.

Описания событий можно искать в документации pygame, но иногда проще вывести события с помощью кода выше и найти событие, которое возникает, когда происходит то, на что вы хотите реагировать.

События клавиатуры

Эта программа может обрабатывать события, где пользователь нажимает на клавиатуре клавишу со стрелкой вправо или влево. Программа выводит, какая клавиша была нажата.

import pygame

pygame.init()
window = pygame.display.set_mode((640, 480))

while True:
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                print("left")
            if event.key == pygame.K_RIGHT:
                print("right")

        if event.type == pygame.QUIT:
            exit()

Константы pygame.K_LEFT и pygame.K_RIGHT относятся к клавишам со стрелками влево и вправо. Константы клавиш pygame для разных клавиш на клавиатуре перечислены в документации pygame.

Например, если пользователь дважды нажмет стрелку вправо, затем один раз стрелку влево, а затем еще раз стрелку вправо, программа выведет:

right
right
left
right

Теперь у нас есть все инструменты, необходимые для перемещения персонажа, или спрайта, по экрану вправо и влево с помощью клавиш со стрелками. Следующий код делает именно это:

import pygame

pygame.init()
window = pygame.display.set_mode((640, 480))

robot = pygame.image.load("robot.png")
x = 0
y = 480-robot.get_height()

while True:
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                x -= 10
            if event.key == pygame.K_RIGHT:
                x += 10

        if event.type == pygame.QUIT:
            exit()

    window.fill((0, 0, 0))
    window.blit(robot, (x, y))
    pygame.display.flip()

В зависимости от того, как вы используете клавиши со стрелками, запуск программы может выглядеть так:

В коде выше у нас есть переменные x и y, которые содержат координаты положения спрайта. Переменная y задана так, чтобы спрайт появлялся внизу окна. Значение y не меняется в течение выполнения программы. А значение x увеличивается на 10 каждый раз, когда пользователь нажимает стрелку вправо, и уменьшается на 10 каждый раз, когда нажимается стрелка влево.

Программа в целом работает довольно хорошо, но клавишу нужно нажимать снова каждый раз, когда мы хотим снова сдвинуться. Было бы лучше, если бы движение продолжалось, пока клавиша удерживается. Следующая программа предлагает такую функциональность:

import pygame

pygame.init()
window = pygame.display.set_mode((640, 480))

robot = pygame.image.load("robot.png")
x = 0
y = 480-robot.get_height()

to_right = False
to_left = False

clock = pygame.time.Clock()

while True:
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                to_left = True
            if event.key == pygame.K_RIGHT:
                to_right = True

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                to_left = False
            if event.key == pygame.K_RIGHT:
                to_right = False

        if event.type == pygame.QUIT:
            exit()

    if to_right:
        x += 2
    if to_left:
        x -= 2

    window.fill((0, 0, 0))
    window.blit(robot, (x, y))
    pygame.display.flip()

    clock.tick(60)

Код теперь содержит переменные to_right и to_left. Они хранят информацию о том, должен ли спрайт двигаться вправо или влево в данный момент. Когда пользователь нажимает клавишу со стрелкой, значение в соответствующей переменной становится True. Когда клавиша отпускается, значение меняется на False.

Часы используются для синхронизации движений спрайта, так что они потенциально происходят 60 раз в секунду. Если нажата клавиша со стрелкой, спрайт сдвигается на два пикселя вправо или влево. Это означает, что спрайт движется со скоростью 120 пикселей в секунду, если клавиша удерживается нажатой.

Loading
Loading
Loading

События мыши

Следующий код реагирует на события, где кнопка мыши нажата, пока курсор находится в области окна:

import pygame

pygame.init()
window = pygame.display.set_mode((640, 480))

while True:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            print("you pressed the button number", event.button, "at location", event.pos)

        if event.type == pygame.QUIT:
            exit()

Выполнение этой программы должно выглядеть примерно так:

you pressed the button number 1 at location (82, 135)
you pressed the button number 1 at location (369, 135)
you pressed the button number 1 at location (269, 297)
you pressed the button number 3 at location (515, 324)

Кнопка номер 1 означает левую кнопку мыши, а кнопка номер 3 означает правую кнопку мыши.

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

import pygame

pygame.init()
window = pygame.display.set_mode((640, 480))

robot = pygame.image.load("robot.png")

while True:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            x = event.pos[0]-robot.get_width()/2
            y = event.pos[1]-robot.get_height()/2

            window.fill((0, 0, 0))
            window.blit(robot, (x, y))
            pygame.display.flip()

        if event.type == pygame.QUIT:
            exit()

Выполнение программы может выглядеть так:

Следующая программа содержит анимацию, где спрайт робота следует за курсором мыши. Положение спрайта хранится в переменных robot_x и robot_y. Когда мышь движется, ее положение сохраняется в переменных target_x и target_y. Если робот не находится в этом месте, он движется в нужном направлении.

import pygame

pygame.init()
window = pygame.display.set_mode((640, 480))

robot = pygame.image.load("robot.png")

robot_x = 0
robot_y = 0
target_x = 0
target_y = 0

clock = pygame.time.Clock()

while True:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEMOTION:
            target_x = event.pos[0]-robot.get_width()/2
            target_y = event.pos[1]-robot.get_height()/2

        if event.type == pygame.QUIT:
            exit(0)

    if robot_x > target_x:
        robot_x -= 1
    if robot_x < target_x:
        robot_x += 1
    if robot_y > target_y:
        robot_y -= 1
    if robot_y < target_y:
        robot_y += 1

    window.fill((0, 0, 0))
    window.blit(robot, (robot_x, robot_y))
    pygame.display.flip()

    clock.tick(60)

Выполнение программы должно выглядеть примерно так:

Loading
Loading
Вы дошли до конца этого раздела! Перейти к следующему разделу:

Текущие баллы можно посмотреть в синем индикаторе в правом нижнем углу страницы.

В этой части:
  1. 1. Pygame

  2. 2. Анимация

  3. 3. События

  4. 4. Больше приемов pygame