PyGame Keyboard Input
In this section we will look at how you can get keyboard input in PyGame and use that to interact with elements in your game. We will then come back to images and text which we will use to make your games more engaging.
Getting Set Up
Before we begin, let’s create a new file (call it movement.py) and copy in the template code from earlier on.
We’ll also add in a rectangle to the screen which will represent our character for now (we will build it to be more impressive with an image in a later tutorial).
- # Colours
- CHARACTER = (255, 30, 70)
- looping = True
- characterX = 10
- characterY = 30
- characterWidth = 50
- characterHeight = 70
- # Processing
- character = pygame.Rect(characterX, characterY, characterWidth, characterHeight)
- WINDOW.fill(BACKGROUND)
- pygame.draw.rect(WINDOW, CHARACTER, character)
Once you’ve saved your file with the template code in it and the addititions detailed above, run the file to make sure you’ve set it up ok. You should have a white window with a red rectangle at the top left which is going to represent our character.
He’s not very exciting right now but we’ll work on that later.
You’ll notice we’ve changed the values for the X and Y coordinate of the character to be variables rather than hard coded values. This is so that we can easily manipulate these values. We set the initial values for these variables outside the loop so that they get set up once before the game loop begins.
Events
Keyboard input is handled within PyGame as events. There are a few ways in which we can handle these events. Each has their own characteristics and is suited to different situations.
get_pressed
The method you will probably use the most is by making use of the following command :
Running this command will set the variable keys to a dictionary with all the keys on the keyboard. The value for each item will be either True if the key is currently pressed, or False if the key is currently not pressed.
Add the following code to your file just below the for loop for events :
- pygame.quit()
- sys.exit()
- pressed = pygame.key.get_pressed()
- if (pressed[K_RIGHT] or pressed[K_d]) :
- characterX = characterX + 3
If you save and run the script you will notice that you can press either the Right arrow key or the d key and your character will move to the right. You can hold either of the keys down and the character will move continuously.
Increase and decrease the value that gets added to characterX and see what effect this has on the movement of your character.
Now let’s make it so that our character can also move to the left :
- pygame.quit()
- sys.exit()
- pressed = pygame.key.get_pressed()
- if (pressed[K_RIGHT] or pressed[K_d]) :
- characterX = characterX + 3
- if (pressed[K_LEFT] or pressed[K_a]) :
- characterX = characterX — 3
We have implemented this as a series of if statements instead of if, elif statements as the former will treat them as separate tests allowing us to act upon several key presses simultaneously whereas the latter would only act upon the first True item. By doing this with if statements we could hold down Right and Down together to do diagonally for instance.
Now we will add in up and down as well. Have a go at doing this by yourself but if you get stuck you can reveal the code below.
- pygame.quit()
- sys.exit()
- pressed = pygame.key.get_pressed()
- if (pressed[K_RIGHT] or pressed[K_d]) :
- characterX = characterX + 3
- if (pressed[K_LEFT] or pressed[K_a]) :
- characterX = characterX — 3
- if (pressed[K_DOWN] or pressed[K_s]) :
- characterY = characterY + 3
- if (pressed[K_UP] or pressed[K_w]) :
- characterY = characterY — 3
Running your code now your character should be able to move in all four directions as well as diagonally.
KEYDOWN and KEYUP
The other method by which we can respond to key presses is to use the events which are fired when a key is pressed. Unlike the previous method they will only fire once, even if you hold the key down.
Let’s make it so that if we press the r key it will reset the position of the character.
- pygame.quit()
- sys.exit()
- if event.type == pygame.KEYDOWN and event.key == K_r :
- characterX = 10
- characterY = 30
- pressed = pygame.key.get_pressed()
If you save and run your code now you should find that when you press the r key on the keyboard your character moves back to it’s starting position. If you press and hold the r key and press other movement keys you should move around without constantly being reset back to the starting position.
Now let’s get the character to move to another position when the r key is released.
- if event.type == pygame.KEYDOWN and event.key == K_r :
- characterX = 10
- characterY = 30
- if event.type == pygame.KEYUP and event.key == K_r :
- characterX = 340
- characterY = 200
- pressed = pygame.key.get_pressed()
Now when you press and hold the r key the character will move to the starting position, you can move your character around and when you release the r key the character will move to the bottom right of the window.
Boundaries
At the moment our character has free reign of movement. If we hold the keys down our character will happily travel beyond the window borders. Let’s fix that and keep him in the window.
There are a few ways in which we can achieve this (in fact nearly unlimited if you’re creative enough) but we’ll look at two of the most common ways. It might be worth making a copy of your file as a backup at this point.
Edge is a wall
The first method involves treating the edge of the window like a wall. When the character hits the wall they can’t travel any further in that direction. We implement this as a series of if statementes in the processing section of our game loop that check if the character is out of bounds and moves them back if so.
- # Processing
- if (characterX + characterWidth > WINDOW_WIDTH) :
- characterX = characterX — 3
- character = pygame.Rect(characterX, characterY, characterWidth, characterHeight)
If you run your code now your character shouldn’t be able to move off the right side of the window.
We have to do a little bit of mathematics here as the coordinates for our rectangle are actually for the top left of the rectangle and we need to check if the right edge of the rectangle is touching the right edge of the window or not.
Now have a go and see if you can stop your character from moving off the left side of the window.
- # Processing
- if (characterX + characterWidth > WINDOW_WIDTH) :
- characterX = characterX — 3
- if (characterX WINDOW_HEIGHT) :
- characterY = 0
- character = pygame.Rect(characterX, characterY, characterWidth, characterHeight)
Now have a go and see if you can get your character to pop out the bottom of the window when they hit the top of the window.
- Section Breakdown
- Introduction
- Getting Set Up
- Events
- get_pressed
- KEYDOWN and KEYUP
- Boundaries
- Activities
- Next Section
- Images
- Coming Soon
Как обрабатывать события от клавиатуры
Любая, даже самая простая игра предполагает взаимодействие с пользователем. Часто для этого используется клавиатура (или тачпад) или мышь. На этом занятии мы с вами увидим как происходит обработка событий от клавиатуры и какие нюансы здесь существуют.
Вообще, за обработку событий отвечает модуль
и ранее мы уже познакомились с методом
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 обрабатывает такие клавиши-модификаторы несколько иначе стандартных клавиш, отсюда и получаются такие особенности. Вот так в целом выполняется обработка событий от клавиатуры. На следующем занятии мы рассмотрим обработку событий от мыши.