Pygame- detect if a key is held down?
Question:
So I am doing a simple thing, and have been following a tutorial on youtube. I have the ability to move the banshee (I used an image from Halo for my ship) using WASD, but I have to repetitively tap keys, whereas I want to be able to move it by holding down the keys. Here’s the code;
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((1440,900))
pygame.display.update()
black=(0,0,0)
white=(255,255,255)
##loads the background
background = pygame.image.load("G:/starfield.jpg")
###loads sprite of a spaceship that will move.
banshee = pygame.image.load("G:/banshee.png")
x=1
y=1
while True:
gamexit = False
while not gamexit:
screen.blit(background,(0,0))
screen.blit(banshee,(x,y))
pygame.display.update()
# if it touches the sides of the window, the window closes
if x==1440 or x==0 or y==900 or y==0:
pygame.quit()
quit()
else:
for event in pygame.event.get():
pressed= pygame.key.get_pressed()
if event.type == pygame.QUIT:
gamexit=True
pygame.quit()
quit()
elif event.type==KEYDOWN:
# moves banshee up if w pressed, same for the other WASD keys below
if event.key==K_w:
y=y-5
x=x
screen.blit(banshee,(x,y))
pygame.display.update()
elif event.key==K_a:
x=x-5
y=y
screen.blit(banshee,(x,y))
pygame.display.update()
elif event.key==K_d:
x=x+5
y=y
screen.blit(banshee,(x,y))
pygame.display.update()
elif event.key==K_s:
y=y+5
x=x
screen.blit(banshee,(x,y))
pygame.display.update()
I have tried many different methods of doing this (On here and elsewhere), to little avail. Is there anything I can do here that doesn’t need to rewrite a large section of code?
Thanks.
Answers:
As a first go, try adding variables dx
and dy
to store the state of the keys
elif event.type==KEYDOWN:
# moves banshee up if w pressed,
# same for the other WASD keys below
if event.key==K_w:
dy = -5
elif event.key==K_a:
dx = -5
elif event.key==K_d:
dx = 5
elif event.key==K_s:
dy = 5
elif event.type==KEYUP:
# moves banshee up if w pressed,
# same for the other WASD keys below
if event.key==K_w:
dy = 0
elif event.key==K_a:
dx = 0
elif event.key==K_d:
dx = 0
elif event.key==K_s:
dy = 0
x += dx
y += dy
screen.blit(banshee,(x,y))
pygame.display.update()
It’s way easier to use pygame.key.get_pressed()
to check if a key is held down, since you don’t need keep track of the state of the keys yourself with events.
I usually create a dictionary that maps keys to the direction they should move the object.
This way, it’s easy to query the result of pygame.key.get_pressed()
.
You can use some easy vector math to normalize the movement direction, so you move diagonally at the same speed as along only the x or y axis.
Also it’s easier to use a Rect
to store the position of your object, since pygame offers a lot of useful functions that work with the Rect
class.
import pygame, math
from pygame.locals import *
# some simple functions for vetor math
# note that the next pygame release will ship with a vector math module included!
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def add(u, v):
return [ u[i]+v[i] for i in range(len(u)) ]
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
pygame.init()
screen = pygame.display.set_mode((1440,900))
screen_r = screen.get_rect()
pygame.display.update()
black=(0,0,0)
white=(255,255,255)
background = pygame.image.load("G:/starfield.jpg")##loads the background
# here we define which button moves to which direction
move_map = {pygame.K_w: ( 0, -1),
pygame.K_s: ( 0, 1),
pygame.K_a: (-1, 0),
pygame.K_d: ( 1, 0)}
banshee = pygame.image.load("G:/banshee.png")
# create a Rect from the Surface to store the position of the object
# we can pass the initial starting position by providing the kwargs top and left
# another useful feature is that when we create the Rect directly from the
# Surface, we also have its size stored in the Rect
banshee_r = banshee.get_rect(top=50, left=50)
speed = 5
gameexit = False
while not gameexit:
# exit the game when the window closes
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameexit = True
# if it touches the sides of the window, end the game
# note how easy this part becomes if you use the Rect class
if not screen_r.contains(banshee_r):
gameexit = True
# get all pressed keys
pressed = pygame.key.get_pressed()
# get all directions the ship should move
move = [move_map[key] for key in move_map if pressed[key]]
# add all directions together to get the final direction
reduced = reduce(add, move, (0, 0))
if reduced != (0, 0):
# normalize the target direction and apply a speed
# this ensures that the same speed is used when moving diagonally
# and along the x or y axis. Also, this makes it easy to
# change the speed later on.
move_vector = [c * speed for c in normalize(reduced)]
# move the banshee
# Another useful pygame functions for Rects
banshee_r.move_ip(*move_vector)
screen.blit(background,(0,0))
# we can use banshee_r directly for drawing
screen.blit(banshee, banshee_r)
pygame.display.update()
So I am doing a simple thing, and have been following a tutorial on youtube. I have the ability to move the banshee (I used an image from Halo for my ship) using WASD, but I have to repetitively tap keys, whereas I want to be able to move it by holding down the keys. Here’s the code;
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((1440,900))
pygame.display.update()
black=(0,0,0)
white=(255,255,255)
##loads the background
background = pygame.image.load("G:/starfield.jpg")
###loads sprite of a spaceship that will move.
banshee = pygame.image.load("G:/banshee.png")
x=1
y=1
while True:
gamexit = False
while not gamexit:
screen.blit(background,(0,0))
screen.blit(banshee,(x,y))
pygame.display.update()
# if it touches the sides of the window, the window closes
if x==1440 or x==0 or y==900 or y==0:
pygame.quit()
quit()
else:
for event in pygame.event.get():
pressed= pygame.key.get_pressed()
if event.type == pygame.QUIT:
gamexit=True
pygame.quit()
quit()
elif event.type==KEYDOWN:
# moves banshee up if w pressed, same for the other WASD keys below
if event.key==K_w:
y=y-5
x=x
screen.blit(banshee,(x,y))
pygame.display.update()
elif event.key==K_a:
x=x-5
y=y
screen.blit(banshee,(x,y))
pygame.display.update()
elif event.key==K_d:
x=x+5
y=y
screen.blit(banshee,(x,y))
pygame.display.update()
elif event.key==K_s:
y=y+5
x=x
screen.blit(banshee,(x,y))
pygame.display.update()
I have tried many different methods of doing this (On here and elsewhere), to little avail. Is there anything I can do here that doesn’t need to rewrite a large section of code?
Thanks.
As a first go, try adding variables dx
and dy
to store the state of the keys
elif event.type==KEYDOWN:
# moves banshee up if w pressed,
# same for the other WASD keys below
if event.key==K_w:
dy = -5
elif event.key==K_a:
dx = -5
elif event.key==K_d:
dx = 5
elif event.key==K_s:
dy = 5
elif event.type==KEYUP:
# moves banshee up if w pressed,
# same for the other WASD keys below
if event.key==K_w:
dy = 0
elif event.key==K_a:
dx = 0
elif event.key==K_d:
dx = 0
elif event.key==K_s:
dy = 0
x += dx
y += dy
screen.blit(banshee,(x,y))
pygame.display.update()
It’s way easier to use pygame.key.get_pressed()
to check if a key is held down, since you don’t need keep track of the state of the keys yourself with events.
I usually create a dictionary that maps keys to the direction they should move the object.
This way, it’s easy to query the result of pygame.key.get_pressed()
.
You can use some easy vector math to normalize the movement direction, so you move diagonally at the same speed as along only the x or y axis.
Also it’s easier to use a Rect
to store the position of your object, since pygame offers a lot of useful functions that work with the Rect
class.
import pygame, math
from pygame.locals import *
# some simple functions for vetor math
# note that the next pygame release will ship with a vector math module included!
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def add(u, v):
return [ u[i]+v[i] for i in range(len(u)) ]
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
pygame.init()
screen = pygame.display.set_mode((1440,900))
screen_r = screen.get_rect()
pygame.display.update()
black=(0,0,0)
white=(255,255,255)
background = pygame.image.load("G:/starfield.jpg")##loads the background
# here we define which button moves to which direction
move_map = {pygame.K_w: ( 0, -1),
pygame.K_s: ( 0, 1),
pygame.K_a: (-1, 0),
pygame.K_d: ( 1, 0)}
banshee = pygame.image.load("G:/banshee.png")
# create a Rect from the Surface to store the position of the object
# we can pass the initial starting position by providing the kwargs top and left
# another useful feature is that when we create the Rect directly from the
# Surface, we also have its size stored in the Rect
banshee_r = banshee.get_rect(top=50, left=50)
speed = 5
gameexit = False
while not gameexit:
# exit the game when the window closes
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameexit = True
# if it touches the sides of the window, end the game
# note how easy this part becomes if you use the Rect class
if not screen_r.contains(banshee_r):
gameexit = True
# get all pressed keys
pressed = pygame.key.get_pressed()
# get all directions the ship should move
move = [move_map[key] for key in move_map if pressed[key]]
# add all directions together to get the final direction
reduced = reduce(add, move, (0, 0))
if reduced != (0, 0):
# normalize the target direction and apply a speed
# this ensures that the same speed is used when moving diagonally
# and along the x or y axis. Also, this makes it easy to
# change the speed later on.
move_vector = [c * speed for c in normalize(reduced)]
# move the banshee
# Another useful pygame functions for Rects
banshee_r.move_ip(*move_vector)
screen.blit(background,(0,0))
# we can use banshee_r directly for drawing
screen.blit(banshee, banshee_r)
pygame.display.update()