pygame - Unpress key on event.key - python

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.

Related

Why isn't 'type' attribute working with event object in Python [duplicate]

This question already has answers here:
How to get keyboard input in pygame?
(11 answers)
How can I make a sprite move when key is held down
(6 answers)
Closed 1 year ago.
I am a newbie to pygame so when I type the below code in, it shows that there is no such thing as a 'type' attribute for the event object. Can you please tell me what's the error.
# Importing libraries and other stuff
import pygame
from pygame.locals import *
# defining the funtion for drawing the block
def draw_block():
surface.fill((232,127,7))
surface.blit(block,(block_x,block_y))
pygame.display.flip()
if __name__ == "__main__":
pygame.init()
surface = pygame.display.set_mode((500,500))
surface.fill((232,127,7))
block = pygame.image.load("block_better_3.jpg").convert()
block_x = 100
block_y = 100
surface.blit(block,(block_x,block_y))
pygame.display.flip()
# Making the window run until user input
running = True
while running:
for event in pygame.event.get():
if event.type == K_ESCAPE:
running = False
elif event.type == QUIT:
running = False
elif event.key == K_UP:
block_y -= 10
elif event.key == K_DOWN:
block_y += 10
elif event.key == K_LEFT:
block_x -= 10
elif event.key == K_RIGHT:
block_x +=10
If you want to know when a key is pressed, you need to verify that the event type (event.type) is KEYDOWN and the key attribute of the event object (event.key) is the specific key (K_ESCAPE, K_UP, ...):
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.key == K_UP:
block_y -= 10
elif event.key == K_DOWN:
block_y += 10
elif event.key == K_LEFT:
block_x -= 10
elif event.key == K_RIGHT:
block_x += 10
See also pygame.event module and pygame.key module.
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action or a step-by-step movement.
If you want to achieve a continuously movement, you have to use pygame.key.get_pressed(). pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement:
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
keys = pygame.key.get_pressed()
if keys[K_UP]:
block_y -= 1
if keys[K_DOWN]:
block_y += 1
if keys[K_LEFT]:
block_x -= 1
if keys[K_RIGHT]:
block_x += 1
Further more you need to redraw the scene in every frame:
# Importing libraries and other stuff
import pygame
from pygame.locals import *
# defining the funtion for drawing the block
def draw_block():
surface.fill((232, 127, 7))
surface.blit(block,(block_x, block_y))
pygame.display.flip()
if __name__ == "__main__":
pygame.init()
surface = pygame.display.set_mode((500,500))
clock = pygame.time.Clock()
block = pygame.image.load("block_better_3.jpg").convert()
block_x = 100
block_y = 100
# application loop
running = True
while running:
# limit the frames per second
clock.tick(100)
# handle the events
for event in pygame.event.get():
if event.type == QUIT:
running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
# update the game states and positions of objects
keys = pygame.key.get_pressed()
block_x += (keys[K_RIGHT] - keys[K_LEFT]) * 2
block_y += (keys[K_DOWN] - keys[K_UP]) * 2
# clear the display; draw the scene; update the display
draw_block()
pygame.quit()
The typical PyGame application loop has to:
handle the events by calling either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by calling either pygame.display.update() or pygame.display.flip()
limit the frames per second to limit CPU usage with pygame.time.Clock.tick

pygame.time.set_timer() event reset?

I've got a little question...
Is there a reset of some sort for the pygame.time.set_timer()?
I've got it running okay but the issue is that I need the timer to reset back to 0 whenever a certain action is performed. As it's running now, the timer will trigger the movedownauto event every second(no matter what happens), but what I want it to do is reset when a key is pressed so that it will then wait one second AFTER the key is pressed to trigger the movedownauto event.
It would be ideal if something could be put into the if event.type == USEREVENT: section.
Example below ↓
pygame.time.set_timer(USEREVENT, 1000)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
# Figure out if it was an arrow key. If so
# adjust speed.
if event.key == pygame.K_LEFT:
x_speed = -40
elif event.key == pygame.K_RIGHT:
x_speed = 40
elif event.key == pygame.K_DOWN:
y_speed = 40
# User let up on a key
elif event.type == pygame.KEYUP:
# If it is an arrow key, reset vector back to zero
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_speed = 0
elif event.key == event.key == pygame.K_DOWN:
y_speed = 0
# Move the object according to the speed vector.
x_coord += x_speed
y_coord += y_speed
if event.type == USEREVENT:
movedownauto(screen, x_coord, y_coord)
else:
drawcharacter(screen, x_coord, y_coord)
clock.tick(tickspeed)
pygame.display.flip()
Thanks!
If you want to trigger the USEREVENT only once after a keypress, remove the pygame.time.set_timer(USEREVENT, 1000) line from the top and add pygame.time.set_timer(USEREVENT, 1000, True) inside the elif event.type == pygame.KEYDOWN: block.
If you set the third argument of pygame.time.set_timer to True, the event will be added to the event queue only once (note that this feature is only avaiable in pygame version 2)
If you want the USEREVENT to happen regulary and just "reset" the timer if a KEYDOWN happens, just remove the timer and add it again.
elif event.type == pygame.KEYDOWN:
pygame.time.set_timer(USEREVENT, 0)
pygame.time.set_timer(USEREVENT, 1000)
...

Pygame - when i push a button and then another

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!

Move left or right to fast causes img to stick

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.

I am unable to make an object move from point A to point B without user input using python 2.7 with pygame

I am attempting to make a game where the 'player' (who spawns as the moon furthest to the right) can move around using the 'WASD' keys and enemies fall from the top of the screen in order to hit the player and end the game. I am currently doing this for an assignment and i have set up a test to see whether it is possible for me to do this and so far i have been able to create a 'player' object that is controllable by the 'WASD' keys and make a screen which requires players to press 'SPACE' to play the game. I am having trouble though with being able to have the enemies fall on the 'Y' axis down the screen smoothly. The picture moves down the screen only when the user presses or releases a key, this creates a jagged movement or when the user moves the mouse over the pygame screen, creating a very smooth movement.
#import necessary modules and pygame.
import pygame, sys, random
from pygame.locals import *
#set global variables
pygame.init()
WINDOWWIDTH = 800
WINDOWHEIGHT = 800
BACKGROUNDCOLOUR = (255,255,255)
TEXTCOLOUR = (0,0,0)
FPS = 30
ENEMYMINSIZE = 10
BOMBSAWAY = -1
ENEMYMAXSIZE = 40
ENEMYMINSPEED = 1
ENEMYMAXSPEED = 10
ADDNEWENEMYRATE = 5
PLAYERMOVERATE = 5
FSIZE = 48
BLUE = (0,0,255)
global PLAY
PLAY = False
global fpsClock
fpsClock = pygame.time.Clock()
# set up pygame and GUI
MainClock = pygame.time.Clock()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
windowSurface.fill(BACKGROUNDCOLOUR)
pygame.display.set_caption('Bubble Dash')
enemy = pygame.image.load('player.jpg')
char = pygame.image.load('player.jpg')
# set up fonts
basicFont = pygame.font.SysFont(None, 48)
# set up the text
text = basicFont.render('Press any key to play!', True, (255,255,0))
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
# draw the text onto the surface
# set up x and y coordinates
# music
windowSurface.blit(text, textRect)
def playgame(PLAY):
x,y = 0,0
movex,movey = 0,0
charx=300
chary=200
direction = 'down'
enemyx= 10
enemyy=10
while PLAY:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == ord('m'):
pygame.mixer.music.stop()
if event.key == ord('n'):
pygame.mixer.music.play()
if event.key == ord('a'):
movex = -0.5
if event.key == ord('d'):
movex = 0.5
if event.key == ord('w'):
movey = -0.5
if event.key == ord('s'):
movey = 0.5
if event.type ==KEYUP:
if event.key == ord('a'):
movex = 0
if event.key == ord('d'):
movex = 0
if event.key == ord('w'):
movey = 0
if event.key == ord('s'):
movey = 0
if direction == 'down':
enemyy += 7
windowSurface.fill(BLUE)
windowSurface.blit(char, (charx, chary))
windowSurface.blit(enemy, (enemyx, enemyy))
pygame.display.update()
charx+=movex
chary+=movey
def playertopresskey(PLAY):
while True: # main game loop
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_SPACE:
PLAY = True
if PLAY == True:
playgame(PLAY)
pygame.display.update()
playertopresskey(PLAY)
I would like for the 'enemy' object to be able to fall from the top without the user either needing to keypress or to key up or to have to move the mouse on the screen, rather the 'enemy' would just fall from the top as soon as the subroutine is called.
You may need to tweak it a bit because it is set up for me at the moment but i deleted a few things that would give you bother. Hopefully someone can help me. Thanks.
The below is link to a picture similar to mine which you can download and replace in the code for both the 'char' and the 'enemy' variables to view this yourself for i cannot access the courses at the present time.
http://www.roleplaygateway.com/roleplay/the-five-elements/characters/miss-joy/image
I found your error, you would have caught it yourself if you would have divided your code into some functions. Here is the problem:
for event in pygame.event.get():
if event.type == QUIT:
...
if event.type == KEYDOWN:
...
if event.type == KEYUP:
...
if direction == 'down':
enemyy += 7
your code moving the enemy is called only when an event is waiting in the queue. Move it out of the loop, and you will be good to go.
You have to change indention for:
if direction == 'down':
enemyy += 7
Now it is inside for event in pygame.event.get():

Categories

Resources