Любая, даже
самая простая игра предполагает взаимодействие с пользователем. Часто для этого
используется клавиатура (или тачпад) или мышь. На этом занятии мы с вами увидим
как происходит обработка событий от клавиатуры и какие нюансы здесь существуют.
Вообще, за
обработку событий отвечает модуль
pygame.event
и ранее мы уже
познакомились с методом
pygame.event.get()
который
возвращал коллекцию событий, произошедших с момента его последнего вызова. Причем,
каждое конкретное событие – это объект, унаследованный от базового класса Event. Этот базовый
класс определяет свойство type, в котором сохраняется тип события. Остальные свойства объектов событий зависят от их типа
(для клавиатуры одни, для мыши другие, для джойстика – третьи и т.д.). Причем,
свойство type принимает несколько разных значений для нажатия клавиш, например:
-
event.type == pygame.KEYDOWN – клавиша
нажата;
-
event.type == pygame.KEYUP – клавиша
отпущена.
То есть, тип –
это не только тип события, но и его конкретизация – какое именно событие
произошло. Давайте в качестве примера создадим программу, которая при нажатии
на курсорные клавиши вправо-влево будет перемещать прямоугольник на экране:
import pygame
pygame.init()
W = 600
H = 400
sc = pygame.display.set_mode((W, H))
pygame.display.set_caption("События от клавиатуры")
pygame.display.set_icon(pygame.image.load("app.bmp"))
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
FPS = 60 # число кадров в секунду
clock = pygame.time.Clock()
x = W // 2
y = H // 2
speed = 5
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x -= speed
elif event.key == pygame.K_RIGHT:
x += speed
sc.fill(WHITE)
pygame.draw.rect(sc, BLUE, (x, y, 10, 20))
pygame.display.update()
clock.tick(FPS)
Смотрите,
нажимая на курсорные клавиши, происходит изменение координаты x и прямоугольник
перерисовывается в новой позиции. Но, если нажать и удерживать клавишу нажатой,
то объект сместится только один раз. Постоянного перемещения не происходит, как
можно было ожидать. Все дело в том, что при нажатии клавиши в PyGame формируется
только одно событие pygame.KEYDOWN. После того,
как мы его прочитали из очереди и обработали, повторного такого события там уже
нет и, соответственно, условие event.type == pygame.KEYDOWN не срабатывает.
Если мы все же
хотим, чтобы при удержании клавиши, прямоугольник постоянно перемещался, то, конечно,
это можно реализовать так:
flLeft = flRight = False
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
flLeft = True
elif event.key == pygame.K_RIGHT:
flRight = True
elif event.type == pygame.KEYUP:
if event.key in [pygame.K_LEFT, pygame.K_RIGHT]:
flLeft = flRight = False
if flLeft:
x -= speed
elif flRight:
x += speed
sc.fill(WHITE)
pygame.draw.rect(sc, BLUE, (x, y, 10, 20))
pygame.display.update()
Здесь мы по
событию pygame.KEYDOWN изменяем
состояния флагов flLeft или flRight в зависимости
от нажатия на левую или правую курсорные клавиши. А в основном цикле по этим
флагам осуществляем изменение координат прямоугольника. Теперь, он перемещается
непрерывно, удерживая клавишу нажатой. При отпускании клавиши генерируется
событие pygame.KEYUP и флаги flLeft, flRight устанавливаются
в False, движение
прекращается.
У вас может
возникнуть вопрос: а зачем нам вторая проверка
if event.key in [pygame.K_LEFT, pygame.K_RIGHT]
Это защита от
двойных нажатий. Например, удерживая нажатой курсорную клавишу, мы нажимаем, а
потом отпускаем еще какую-нибудь. Тогда без этой второй проверки по событию pygame.KEYUP флаги flLeft, flRight станут равными False и движение
остановится. Хотя, курсорная клавиша остается нажатой. Чтобы этого не
происходило и делается эта дополнительная проверка.
Однако, тот же
самый функционал можно реализовать проще, используя модуль работы с клавиатурой
pygame.key
В частности, в нем
есть функция:
pygame.key.get_pressed()
которая
возвращает информацию о состояниях клавиш в виде кортежа:
Если клавиша с
определенным индексом нажата, то в этом кортеже ее значение будет 1, а если
отжата, то 0. Используя эту функцию, наша программа может быть записана в таком
виде:
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= speed
elif keys[pygame.K_RIGHT]:
x += speed
sc.fill(WHITE)
pygame.draw.rect(sc, BLUE, (x, y, 10, 20))
pygame.display.update()
clock.tick(FPS)
Как видите, все
стало предельно простым. Фактически, функция get_pressed() дает маску
нажатых на клавиатуре клавиш и никак не влияет на состояния событий,
находящихся в очереди. То есть, мы здесь напрямую не работаем с событиями,
зарегистрированные в PyGame, а просто используем информацию о
том, какая клавиша нажата. И это имеет свои следствия. Например, если мы хотим
перемещать прямоугольник при одновременном нажатии на клавишу Ctrl и курсорные
клавиши вправо-влево, то с помощью функции get_pressed() нельзя
отследить состояния клавиш-модификаторов:
Shift,
Ctrl, Alt и др.
В выходном
кортеже информации по ним просто нет. Здесь без обработки событий не обойтись.
И лучше всего это сделать вот так:
move = 0
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and event.mod == pygame.KMOD_LCTRL:
move = -speed
elif event.key == pygame.K_RIGHT and event.mod == pygame.KMOD_LCTRL:
move = speed
elif event.type == pygame.KEYUP:
if event.key in [pygame.K_LEFT, pygame.K_RIGHT]:
move = 0
x += move
sc.fill(WHITE)
pygame.draw.rect(sc, BLUE, (x, y, 10, 20))
pygame.display.update()
clock.tick(FPS)
При этом важна
последовательность нажатия на клавиши: сначала нужно нажать левый Ctrl, а потом
курсорную клавишу вправо-влево. В другой последовательности работать уже не
будет. Библиотека PyGame обрабатывает такие клавиши-модификаторы
несколько иначе стандартных клавиш, отсюда и получаются такие особенности.
Вот так в целом
выполняется обработка событий от клавиатуры. На следующем занятии мы рассмотрим
обработку событий от мыши.