I am making a spaceship invaders game in python 3 with pygame. I am currently having troubles with the spaceship sticking making me double tap a left or right arrow key for it to take effect. Here is my code:
import pygame
pygame.init()
display_width = 800
display_height = 600
black = (0, 0, 0)
white = (255, 255, 255)
#Window Size
gameDisplay = pygame.display.set_mode((display_width, display_height))
#Title Of Window
pygame.display.set_caption('A Bit Racey')
#FPS
clock = pygame.time.Clock()
spaceshipImg = pygame.image.load('SpaceShipSmall.png')
def spaceship(x,y):
gameDisplay.blit(spaceshipImg, (x,y))
x = (display_width * 0.45)
y = (display_height * 0.8)
x_change = 0
crashed = False
while not crashed:
# this will listen for any event every fps
for event in pygame.event.get():
if event.type == pygame.QUIT:
#change later
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
elif event.key == pygame.K_RIGHT:
x_change = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
x += x_change
gameDisplay.fill(white)
spaceship(x,y)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
Every time a pygame.KEYUP event is detected for the left or right arrow key you reset x_change. Even when you hold down e.g. your right arrow key a single left arrow key press stops the movement of your spaceship.
To solve this problem you could use the pygame.key.get_pressed() method to get the state of all keyboard buttons. This function returns a sequence of boolean values indexed by pygames key constant values representing the state of every key on the keyboard.
Because you donĀ“t need to call pygame.key.get_pressed() every time an event happens, the updated main loop should look like this:
while not crashed:
# this will listen for any event every fps
for event in pygame.event.get():
if event.type == pygame.QUIT:
#change later
crashed = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
#get the state of all keyboard buttons
pressedKeys = pygame.key.get_pressed()
#change position if pygame.K_LEFT or pygame.K_RIGHT is pressed
if pressedKeys[pygame.K_LEFT]:
x += -5
elif pressedKeys[pygame.K_RIGHT]:
x += 5
gameDisplay.fill(white)
spaceship(x,y)
pygame.display.update()
clock.tick(60)
Notice that a pygame.K_LEFT event has a higher priority than a pygame.K_RIGHT event. You could change this behavior by using two separate if blocks. Many thanks to #sloth for pointing this out!:
#change position if either pygame.K_LEFT or pygame.K_RIGHT is pressed
if pressedKeys[pygame.K_LEFT]:
x += -5
if pressedKeys[pygame.K_RIGHT]:
x += 5
I hope this helps you :)
add the following statement before x += x_change:
print (event.type, event.key, x)
this will print the event data and x position to the console.
try your script again, try holding down the left key for 3 seconds, then release, and repeat for the right key. See if you see multiple keydown events while holding down the keys. I think you should only see one keyup event upon release.
Related
This question already has answers here:
How can I make a sprite move when key is held down
(6 answers)
How to get keyboard input in pygame?
(11 answers)
Closed 1 year ago.
It is a bit confusing to explain. So, if you have questions, please ask them. I am creating a game using pygame and my "car" moves when you use the "up","down","left", and "right" keys. However, in the way I have it coded, when I release the right key for example but am still holding the left key down, it will change the movement to 0 and not recognize that the left key is still being held down. It makes the movement rather weird. I am trying to make this movement more smooth so that the game is more enjoyable, any ideas are helpful, thank you.
Here is my code:
import pygame
#Initializes pygame
pygame.init()
#Create the screen
screen = pygame.display.set_mode((1000, 800))
#Icon and Caption
icon = pygame.image.load('redcar.png')
pygame.display.set_icon(icon)
pygame.display.set_caption("Placeholder")
# Car
car = pygame.image.load('redcar.png')
x = 500
y = 600
x_change = 0
y_change = 0
def player(x,y):
screen.blit(car, (x, y))
#Game loop, keep window open unless you quit the window
running = True
while running:
#RGB
screen.fill((255, 255, 255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
#If key is pressed, check what key it was
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -1
if event.key == pygame.K_RIGHT:
x_change = 1
if event.key == pygame.K_UP:
y_change = -1
if event.key == pygame.K_DOWN:
y_change = 1
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
x_change = 0
if event.key == pygame.K_RIGHT:
x_change = 0
if event.key == pygame.K_UP:
y_change = 0
if event.key == pygame.K_DOWN:
y_change = 0
x += x_change
y += y_change
player(x,y)
#For each event, update window
pygame.display.update()
You can solve this by using pygame.key.get_pressed(). That will tell you if a key is being held down. It works when any amount of keys are being held down.
Here's a simplified version of your code to demonstrate how that works:
import pygame
pygame.init()
pygame.font.init();
myfont = pygame.font.SysFont('Consolas', 30)
screen = pygame.display.set_mode((1000, 800))
running = True
while running:
screen.fill((255, 255, 255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
textsurface = myfont.render('Up', False, (0, 0, 0))
screen.blit(textsurface,(0,0))
if pressed[pygame.K_DOWN]:
textsurface = myfont.render('Down', False, (0, 0, 0))
screen.blit(textsurface,(0,50))
if pressed[pygame.K_LEFT]:
textsurface = myfont.render('Left', False, (0, 0, 0))
screen.blit(textsurface,(0,100))
if pressed[pygame.K_RIGHT]:
textsurface = myfont.render('Right', False, (0, 0, 0))
screen.blit(textsurface,(0,150))
pygame.display.update()
If I hold Left and Up the window will show:
You could set x_change and y_change to zero before calling get_pressed(), then set those variables within the ifs, depending on which evaluate to true. e.g. Set x_change to -1 if the left key is being pressed.
You may want to think about what happens when the user is pressing up+down (or left+right). That could either cancel out or one key could take precedence.
I am having a problem with pyGame (complete newbie here). I practice with event.key and would like to unpress / cut off key that is pressed continuously. I have tried to google it and checked this site, but that led to no promising results. Whole thing takes place in
if event.key == pygame.K_UP and isJumping == False:
(full lines 49 - 55)
## HOW TO UNPRESS K_UP IN CASE IT WAS PRESSED AND NOT RELEASED?
if event.key == pygame.K_UP and isJumping == False:
yChange -= 10
isJumping = True
elif event.key == pygame.K_UP and isJumping == True:
print("X")
This works just fine if up arrow is pressed once, but keeps on executing yChange in a loop in case it was not released - that of course is not desired at all. Could you help me with this please?
import pygame
pygame.init() # Initialize pyGame module
# Set screen dimensions
screenW = 800
screenH = 600
# Set color
AQUA = (155, 255, 255)
# Set FPS
FPS = 60
fpsClock = pygame.time.Clock()
# Set character
mainChar = pygame.image.load('p1_front.png')
mainCharX = screenH / 2 # char X position
mainCharY = screenW / 2 # char Y position
isJumping = False
xChange = 0
yChange = 0
MOVSPEED = 1
# Set backgrounds
mainBg = pygame.image.load('bg_castle.png')
mainBg = pygame.transform.scale(mainBg, (screenW, screenH))
# Set the window, param in a tuple is width / height
DISPLAYSURF = pygame.display.set_mode((screenW, screenH))
# Set window name (game title)
pygame.display.set_caption("Newbie training")
while True: # Main loop of the game
#set BG
DISPLAYSURF.blit(mainBg, (0,0))
# Events loop below
for event in pygame.event.get():
if event.type == pygame.QUIT: # Quit window when [x] is pressed
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
xChange += MOVSPEED
elif event.key == pygame.K_LEFT:
xChange -= MOVSPEED
## HOW TO UNPRESS K_UP IN CASE IT WAS PRESSED AND NOT RELEASED?
if event.key == pygame.K_UP and isJumping == False:
yChange -= 10
isJumping = True
elif event.key == pygame.K_UP and isJumping == True:
print("X")
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT:
xChange = 0
if event.key == pygame.K_UP:
yChange = 0
#print(event)
mainCharX += xChange
mainCharY += yChange
DISPLAYSURF.blit(mainChar, (mainCharX, mainCharY)) # Load character
pygame.display.update() #update window
fpsClock.tick(FPS) # FPS goes always after update
If you only want the player to be able to jump 10 'units' per up press then stop in place, you could simply set yChange to 0 each frame after you add it to mainCharY.
However, if you're looking for a more robust jumping system (button is pressed, player moves up for a few frames, then stops and falls back down) more is required.
To give you an idea: what if you had some logic at the end of each frame that checks if mainCharY > 0 (player is not on ground) and, if so, subtracts some number from yChange (as if gravity were pulling them down)?
In the above case, your code should avoid the repeating up press problem. It might be easier to just test (mainCharY > 0) instead of using an isJumping variable.
It seems you're misunderstanding how the event queue and event loop work. When you press a key, a single pygame.KEYDOWN event gets added to the event queue. Calling pygame.event.get empties the event queue and puts all events into a list over which you iterate with the for loop: for event in pygame.event.get():. That means pygame doesn't know that you're still holding the key, it will only see that you've pressed UP once. It will also notice that you release the key and add a pygame.KEYUP event to the queue.
In your case you're decreasing the yChange variable to -10 when you press the UP key. In the while loop mainCharY will then be decremented by -10 each frame and the character will move upwards. As soon as you release the key, the yChange is reset to 0 and the player character stops.
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
yChange = 0
To implement jumping in your game, you should add a GRAVITY constant which you add to your y-velocity each frame to accelerate downwards. When the character touches the ground, set the y-velocity to 0 and a variable (on_ground or something similar) to True, so that you know that the player can jump. When the player presses the jump key, you can set the y-velocity to a negative value and the sprite will start to move upwards until the GRAVITY pulls him down again. Here's a complete example.
I don't think it's likely at all that pygame could miss a key event.
I think the problem is that you're not setting isJumping to false anywhere, which stops the changing of yChange forever after the first press. If I'm interpreting this correctly, the following code should work:
if event.type == pygame.KEYDOWN:
... # Your other code
if event.key == pygame.K_UP and not isJumping: # "== False" is not necessary
yChange -= 10
isJumping = True
if event.type == pygame.KEYUP:
... # Your other code
if event.key == pygame.K_UP and isJumping:
yChange = 0
isJumping = False # <-- This is the addition to the code
One thing to keep in mind is that this jumping system won't look very natural. Look to #SamHolloway's answer for more clarification on this.
I recently started tinkering a bit with pygame and I made this little piece of code (following a tutorial):
import pygame
pygame.init()
display_width = 1920
display_height = 1080
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Galvadon's Adventure")
clock = pygame.time.Clock()
galvadon = pygame.image.load("galvadonModelVersion1.0.png")
def galvadonIsHere(x,y):
gameDisplay.blit(galvadon,(x,y))
x = (display_width * 0.30)
y = (display_height * 0.2)
y_change = 0
crashed = False
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
y_change = 5
elif event.key == pygame.K_UP:
y_change = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
y_change = 0
y += y_change
gameDisplay.fill(white)
galvadonIsHere(x,y)
print(event)
pygame.display.update()
clock.tick(30)
pygame.quit()
quit()
and to an extent this code works, the problem is that after I move the image using the up and down arrows, the image starts to respond to any mouse movement by just gliding in the direction it last moved towards. I probably missed something out, but I just can't seem to find what it is. I looked at various websites looking for the answer but I couldn't find anyone asking a similar question, hence why I make this topic.
There are couple of things that you can do better here. But lets start with fixing the issue.
'Bug' is that after you press keys, y_change is set and never reset for next engine loop. This should help you:
...
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
y_change = 5
elif event.key == pygame.K_UP:
y_change = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
y_change = 0
y += y_change
gameDisplay.fill(white)
galvadonIsHere(x, y)
print(event)
pygame.display.update()
clock.tick(30)
y_change = 0
...
Pay close attention at the last line in my snippet. Here you reset y_change so in next engine loop, whatever happens or doesn't, it wont affect position of your image.
Refactor and improve
First, you can agree that checking if event type is KEY_DOWN and nesting checks if it is particular key button is pain to read and work with. Not to mention you check if event type is KEYUP even though you know that is KEYDOWN here:
if event.type == pygame.KEYDOWN:
...
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
y_change = 0
So, what I do is define helper function, that can be usual one or lambda. Now, many would argue that lambda functions are affecting readability but here it can certainly help you with these checks.
Example:
key_pressed = lambda event, key: event.type == pygame.KEYDOWN and event.key == key
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
break
elif key_pressed(event, pygame.K_DOWN):
y_change = 5
elif key_pressed(event, pygame.K_UP):
y_change = -5
else:
y_change = 0
y += y_change
gameDisplay.fill(white)
galvadonIsHere(x, y)
print(event)
pygame.display.update()
clock.tick(30)
y_change = 0
One more thing, pay attention at condition that there was pygame.QUIT event. break exits the for loop and then while loop ends too. This way you do not process any queued events, nor you update and blit image.
Your problem is in the while loop. The if statement for keyup is in the if statement for keydown, so it is preventing it from working. The following solves your problem:
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
y_change = 5
elif event.key == pygame.K_UP:
y_change = -5
else:
y_change = 0
Hope this helps.
First of all, here is my code:
while not crashed:
curr_event = pygame.event.get()
if (len(curr_event) == 2):
print curr_event[0],'\n',curr_event[1],'\n\n\n'
for event in curr_event:
if event.type == pygame.KEYUP and not len(curr_event) == 1:
continue
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
elif event.key == pygame.K_RIGHT:
x_change = 5
if event.type == pygame.KEYUP:
x_change = 0
x += x_change
Display.fill((255, 255, 255))
car(x,y)
pygame.display.update()
clock.tick(100)
This should be a try to move smoothly from right to left and opposite.
All is good when for example, I press "left key" and wait 2 seconds and then press "right key".
But - when I press "left key", leave the key, and just after it press "right key" - the "key up" is smashing my right press and the player stops on the screen.
I thought it's because created a list of 2 indexes when the first have the "right press" and the second have the "key up", so i tried to do:
if event.type == pygame.KEYUP and not len(curr_event) == 1:
continue
As you can see in my code.
Well... sometimes it's the situation... But sometimes it doesn't creates a list with two indexes - but smash the "right key" pressing..
How can I fix it and what is the issue?
Moving the mouse, pressing buttons and other actions counts as events and are put in the queue, so the code if event.type == pygame.KEYUP and not len(curr_event) == 1: continue might be unreliable.
Solution
You could check the state of the button (if it's being held down or not) rather than just when it's pressed and released. pygame.key.get_pressed() returns a list of all the keys current state: 0 if not being pressed and 1 if it's being pressed. A key position in the list is it's integer constant representation.
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
x_change = -5
elif key[pygame.K_RIGHT]:
x_change = 5
This doesn't keep track on was what pressed first or last, so if you're holding down both keys it'll go left. I've tried another solution that works pretty well. It basically a list that keeps track of the key presses in a list and moves the player based on the last key pressed.
import pygame
pygame.init()
screen = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
x, y = 100, 100
car = pygame.Surface((32, 32))
velocity_dx = []
speed = 5
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
velocity_dx.insert(0, -speed)
elif event.key == pygame.K_RIGHT:
velocity_dx.insert(0, speed)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
velocity_dx.remove(-speed)
elif event.key == pygame.K_RIGHT:
velocity_dx.remove(speed)
try:
x += velocity_dx[0] # Take the first value from the list
except IndexError: # if list is empty, catch the IndexError.
pass
screen.fill((255, 255, 255))
screen.blit(car, (x, y))
pygame.display.update()
# print(velocity_dx) # Uncomment to see what's happening in action!
I am messing around with pygame and I am eventually working towards a pong clone. I implemented player movement with the arrow keys and when ever I switch from going up to immediately going down, my player freezes and won't move again until I press that direction key again. Here is my code:
import sys, pygame
pygame.init()
display_width = 640
display_height = 480
display = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Test Game")
black = (0,0,0)
white = (255,255,255)
clock = pygame.time.Clock()
running = True
class Player:
def __init__(self,x,y,hspd,vspd,color,screen):
self.x = x
self.y = y
self.hspd = hspd
self.vspd = vspd
self.color = color
self.screen = screen
def draw(self):
pygame.draw.rect(self.screen,self.color,(self.x,self.y,32,32))
def move(self):
self.x += self.hspd
self.y += self.vspd
player = Player(0,0,0,0,black,display)
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
player.hspd = 0
if event.key == pygame.K_LEFT:
player.hspd = 0
if event.key == pygame.K_UP:
player.vspd = 0
if event.key == pygame.K_DOWN:
player.vspd = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.hspd = 4
if event.key == pygame.K_LEFT:
player.hspd = -4
if event.key == pygame.K_UP:
player.vspd = -4
if event.key == pygame.K_DOWN:
player.vspd = 4
#Clear the screen
display.fill(white)
#Move objects
player.move()
#Draw objects
player.draw()
#Update the screen
pygame.display.flip()
print "I made it!"
pygame.quit()
sys.exit()
I suggest you work with key.get_pressed() to check for the current set of pressed keys.
In your scenario - when you press down and release up (in that order) - the speed is set to 0, so you need to inspect the keys pressed not just by the current event.
Here is a working version of the relevant part:
def current_speed():
# uses the fact that true = 1 and false = 0
currently_pressed = pygame.key.get_pressed()
hdir = currently_pressed[pygame.K_RIGHT] - currently_pressed[pygame.K_LEFT]
vdir = currently_pressed[pygame.K_DOWN] - currently_pressed[pygame.K_UP]
return hdir * 4, vdir * 4
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
player.hspd, player.vspd = current_speed()
#Clear the screen
display.fill(white)
#Move objects
player.move()
#Draw objects
player.draw()
#Update the screen
pygame.display.flip()
To expand on LPK's answer, your key down (for event.key == pygame.K_DOWN) is likely being processed before your key up (from event.key == pygame.K_UP) is processed. So while both are down (and you can confirm this), you may experience movement, until you release the up key.
your problem is here:
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
player.hspd = 0
if event.key == pygame.K_LEFT:
player.hspd = 0
if event.key == pygame.K_UP:
player.vspd = 0
if event.key == pygame.K_DOWN:
player.vspd = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.hspd = 4
if event.key == pygame.K_LEFT:
player.hspd = -4
if event.key == pygame.K_UP:
player.vspd = -4
if event.key == pygame.K_DOWN:
player.vspd = 4
I am guessing that your key event down is still consumed when u switch the keys immediately, meaning no other key down event is getting triggered as long as the first event didn't fire its key up event yet.
EDIT: maybe its better to check if the player is moving and if so just reverse speed . Then you would only need to check the down event.
Otherwise your event will be consumed and not checked properly.
For your method you would need to store the occurred key events since the last frame and check that list.