I am learning python as well as pygame. I am struggling a bit with finding the issue in my code. When I hold down a key and press another key at the same time, and then let go of them in the same order I press them, it sometimes makes my little character move backwards in the opposite direction.
Here is my code that I am struggling with:
while running:
for event in pygame.event.get():
if event.type == KEYDOWN:
keystate = pygame.key.get_pressed()
if event.type == pygame.QUIT: # if user clicked close
running = False # flag done to exit this loop
# all game logic / controls should go below this comment
elif event.type == KEYDOWN:
if keystate[K_SPACE]:
print ('pressed space')
player.move_up = True
elif keystate[K_RIGHT]:
print ('pressed right')
player.move_right = True
elif keystate[K_LEFT]:
print ('pressed left')
player.move_left = True
elif event.type == KEYUP:
if keystate[K_SPACE]:
print ('released space')
player.move_up = False
elif keystate[K_RIGHT]:
print ('released right')
player.move_right = False
elif keystate[K_LEFT]:
print ('released left')
player.move_left = False
if player.move_up:
player.pos[1] -= 3
if player.move_up == False:
if player.pos[1] < ground_level:
player.pos[1] +=3
if player.move_right:
player.pos[0] += 5
if player.move_left:
player.pos[0] -= 5`
So far I don't have any actual physics, and he just flies around. But that's okay, I just want to iron out the issues that I am already having.
You never update keystate's value on a KEYUP event. Thus, it still has whatever values it had whenever the last KEYDOWN event fired.
To fix this, change this...
if event.type == KEYDOWN:
keystate = pygame.key.get_pressed()
to this:
if event.type in (KEYDOWN, KEYUP):
keystate = pygame.key.get_pressed()
Related
This is the basis of the code where I need help,
I want countmouseclick to = +2 but whenever I try I cant get it right. how would I go about this. hand_rect is the upgrade button and whenever I click it it multiplies the cost and works like it should it just wont at to the clicks.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.MOUSEBUTTONUP:
if llama_rect.collidepoint(event.pos):
countmouseclick += 1
if event.type == pygame.MOUSEBUTTONDOWN:
if countmouseclick >= upgrade_cost:
if hand_rect.collidepoint(event.pos):
countmouseclick -= upgrade_cost
upgrade_cost = round(upgrade_cost*2.5)
if llama_rect.collidepoint(event.pos):
countmouseclick = countmouseclick*2
This should be solved reasonably easily by factoring out the amount of increase to a variable (e.g.: llama_rect_value).
Start the variable at 1, but then once the "upgrade" is used, change this to 2 or whatever.
llama_rect_value = 1 # set the initial value of a click
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
elif event.type == pygame.MOUSEBUTTONUP:
if llama_rect.collidepoint(event.pos):
countmouseclick += llama_rect_value # <<-- HERE
elif event.type == pygame.MOUSEBUTTONDOWN:
if countmouseclick >= upgrade_cost:
if hand_rect.collidepoint(event.pos):
countmouseclick -= upgrade_cost
upgrade_cost = round(upgrade_cost*2.5)
if llama_rect.collidepoint(event.pos):
countmouseclick = countmouseclick*2
llama_rect_value = 2 # <<-- HERE
PS> Don't forget to use if/else/elif rather than lists of if, if, if. When event.type is already matched, there's no point checking if it's something else. If/else/elif handles this automatically.
I'm a beginner in python and I am trying to learn the features of pygame in order to make a simple game. I want to move an image using the wasd keys, but it's not working. Please help.
I'm using python (3.7.0) on windows 10.
The following is the code:
import pygame
from pygame.locals import *
pygame.init()
pygame.display.init()
keys = [False, False, False, False]
screen=pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
background=pygame.Surface(screen.get_size())
background.fill((255,255,255))
playerpos=[100,100]
player=pygame.image.load("Copper.png")
while 1:
pygame.display.init()
screen.fill(0)
screen.blit(background,(0,0))
screen.blit(player,playerpos)
for event in pygame.event.get():
pygame.display.init()
pygame.display.flip()
if event.type == pygame.KEYDOWN:
if event.key==pygame.K_RETURN:
pygame.display.quit()
pygame.display.flip()
# 8 - loop through events
for event in pygame.event.get():
# 9 - check if event is X button
if event.type==pygame.QUIT:
# 10 - quit the game
pygame.quit()
exit(0)
if event.type == pygame.KEYDOWN:
if event.key==K_w:
keys[0]=True
elif event.key==K_a:
keys[1]=True
elif event.key==K_s:
keys[2]=True
elif event.key==K_d:
keys[3]=True
if event.type == pygame.KEYUP:
if event.key==pygame.K_w:
keys[0]=False
elif event.key==pygame.K_a:
keys[1]=False
elif event.key==pygame.K_s:
keys[2]=False
elif event.key==pygame.K_d:
keys[3]=False
# 9 - Move player
if keys[0]:
playerpos[1]-=500
elif keys[2]:
playerpos[1]+=500
if keys[1]:
playerpos[0]-=500
elif keys[3]:
playerpos[0]+=500
I expect the image "Copper.png" to move when I press w,a,s,d but the image does not move. The image does refresh everytime I press w,a,s,d but does not move.
Get rid of the multiple even handling loops. Use a single loop to handle all the events.
Further it is sufficient to init the display once (pygame.display.init())
Create a variable speed, which defines the number of pixels, the position of the image changes by each step
First evaluate the pygame.QUIT event and stop running the main loop when the event happens:
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
Then handle the other events like the pygame.KEYDOWN and pygame.KEYUP.
For a continuously movement, the manipulation of the player position has to be done outside the event loop. If it would be don in the loop, the the position of the player would only change if en event occurs. Note use a small "speed" (speed = 1), else the player would rapidly moved out of the window.
for event in pygame.event.get():
# event handling
if keys[0]:
playerpos[1]-=speed
elif keys[2]:
playerpos[1]+=speed
if keys[1]:
playerpos[0]-=speed
elif keys[3]:
playerpos[0]+=speed
Do the drawing of the scene at the end of main loop:
speed = 1
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key==pygame.K_RETURN:
done = True
for i in range(4):
if event.key == (K_w, K_a, K_s, K_d)[i]:
keys[i]=True
elif event.type == pygame.KEYUP:
for i in range(4):
if event.key == (K_w, K_a, K_s, K_d)[i]:
keys[i]=False
if keys[0]:
playerpos[1]-=speed
elif keys[2]:
playerpos[1]+=speed
if keys[1]:
playerpos[0]-=speed
elif keys[3]:
playerpos[0]+=speed
screen.fill(0)
screen.blit(background,(0,0))
screen.blit(player,playerpos)
pygame.display.flip()
Note, alternatively you can use pygame.key.get_pressed() to get all states of of all keyboard buttons at once. So you don't need to evaluate the key events separately:
speed = 1
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
done = True
allKeys = pygame.key.get_pressed()
playerpos[0] += -speed if allKeys[K_a] else speed if allKeys[K_d] else 0
playerpos[1] += -speed if allKeys[K_w] else speed if allKeys[K_s] else 0
screen.fill(0)
screen.blit(background,(0,0))
screen.blit(player,playerpos)
pygame.display.flip()
Okay, so I've researched this topic a bit. I'm creating a game in Python's Pygame, a replica of the famous "Raiden 2". My game loop is fairly similar to those I've seen around. What I'm trying to do is have the constructor create a bullet object (with my Bullet class) while the space bar is being held. However, the following code only creates a single bullet per keypress. Holding the button does nothing, just creates a single bullet.
while game.play is True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
b = Bullet(x, y)
bullet_group.add(b)
bullet_group.draw(screen)
Not sure where to go from here. Any help is welcome and appreciated.
This should work as you expect it:
addBullets = False
while game.play is True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
addBullets = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
addBullets = False
if addBullets:
b = Bullet(x, y)
bullet_group.add(b)
bullet_group.draw(screen)
You move the creation of bullets out of the handling of events and set there only a "flag" which is taken back if the key is released stopping creation of bullets.
And ... as mentioned in the comment by jsbueno this will work too:
while game.play is True:
for event in pygame.event.get():
pass # do something with events
if pygame.key.get_pressed()[pygame.K_SPACE]:
b = Bullet(x, y)
bullet_group.add(b)
I think the other answer is lacking an important detail. You most likely don't want to fire a bullet every frame, so you need to have some kind of timer. The simplest way is to count the frames, but then the game would be frame rate bound (better don't do it in this way).
firing = False
bullet_timer = 0
while game.play:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game.play = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
firing = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
firing = False
if bullet_timer > 0:
bullet_timer -= 1
elif firing: # Timer is less than 0 and we're firing.
bullet = Bullet()
bullet_group.add(bullet)
bullet_timer = 10 # Reset timer to 10 frames.
The frame rate independent variant is to use the time that pygame.Clock.tick returns (the time that has passed since it was called the last time) and subtract it from the timer variable. If the timer is <= 0 we're ready to fire.
clock = pygame.time.Clock()
firing = False
bullet_timer = 0
while game.play:
# `dt` is the passed time since the last tick in seconds.
dt = clock.tick(30) / 1000
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
firing = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
firing = False
if bullet_timer > 0:
bullet_timer -= dt
elif firing: # Timer is below 0 and we're firing.
bullet = Bullet()
bullet_group.add(bullet)
bullet_timer = .5 # Reset timer to a half second.
I am making this multiple choice program, but I need the mouse event to only work after enter is pressed.
Here is my code:
for event in pygame.event.get(): # If user did something
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
Then down below I have this:
for event in pygame.event.get(): # If user did something
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
print("Derp")
Derp won't print when I press left mouse button.
However, when I have it indented like this:
for event in pygame.event.get(): # If user did something
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
print("Derp")
Derp does print when I press left mouse button
You could use a boolean indicating wheter or not enter was pressed.
for event in pygame.event.get(): # If user did something
enter_pressed = False
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and enter_pressed:
print("Derp")
I think that the problem is that you are iterating over all events, and inside that loop you are iterating over all events (again), and that causes some problems
It is hard to tell from what you said, but I have a couple of recommendations for you. First, make sure that the for loop with derp isn't in any if statements you don't want it to be in. Second, make sure you call pygame.event.get() once per game loop or the code will only give you events that happened between the two calls, which will not be all of them. If neither of these things work, try posting the whole code.
I'm attempting to work out simple controls for an application using pygame in Python. I have got the basics working, but I'm hitting a weird wall: I am using the arrow keys to control my character. If I hold down one arrow key, then hold down another arrow key (to move diagonally), the character moves as expected. However, if I release the second key that I pressed (while still holding down the first key), the character stops moving, even though I am still holding down that first key. Here is my simple movement code:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if pygame.key.get_pressed()[K_LEFT]:
player.pos = (player.pos[0] - 2, player.pos[1])
if pygame.key.get_pressed()[K_RIGHT]:
player.pos = (player.pos[0] + 2, player.pos[1])
if pygame.key.get_pressed()[K_UP]:
player.pos = (player.pos[0], player.pos[1] - 2)
if pygame.key.get_pressed()[K_DOWN]:
player.pos = (player.pos[0], player.pos[1] + 2)
Now, I was naturally very confused by this. So I tried to print some lines to debug. In the top of the main control loop, I wrote:
print (pygame.key.get_pressed()[K_DOWN], pygame.key.get_pressed()[K_RIGHT])
print pygame.event.get()
...to output a tuple displaying the state of the down and right arrow keys, and then display the pygame event queue. My results baffled me even more. If I move the character diagonally down and right, pressing the down key first and then the right key, then release the right key to make it move simply downward, the character stops moving as before... but this is printed to the shell:
(1, 0)
[]
That is, when I release the right arrow key and still hold down the down arrow key, pygame.key.get_pressed() understands that the down arrow key is still being held down, but there is nothing in the event queue.
Also, earlier in the code (before the control loop) I am invoking
pygame.key.set_repeat(1, 2)
to make the character continue to move while the key is held down.
Any help will be appreciated! Thanks :)
For things like movement, you should not check for events (like KEYDOWN or KEYUP), but check every iteration of your mainloop if your movement keys are pressed (using get_pressed).
In your code, you check the pressed keys only if there's also a KEYDOWN event.
There are also some other things to consider:
You should seperate the key-mapping and the speed of your player, so it will be easier later on to change either of this.
You should determine a movement vector and normalize it first, since otherwise, if your vertical and horizontal movement speed is 10, your diagonal movement speed would be ~14.
Working example:
import pygame
pygame.init()
screen = pygame.display.set_mode((200, 200))
run = True
pos = pygame.Vector2(100, 100)
clock = pygame.time.Clock()
# speed of your player
speed = 2
# key bindings
move_map = {pygame.K_LEFT: pygame.Vector2(-1, 0),
pygame.K_RIGHT: pygame.Vector2(1, 0),
pygame.K_UP: pygame.Vector2(0, -1),
pygame.K_DOWN: pygame.Vector2(0, 1)}
while run:
for e in pygame.event.get():
if e.type == pygame.QUIT: run = False
screen.fill((30, 30, 30))
# draw player, but convert position to integers first
pygame.draw.circle(screen, pygame.Color('dodgerblue'), [int(x) for x in pos], 10)
pygame.display.flip()
# determine movement vector
pressed = pygame.key.get_pressed()
move_vector = pygame.Vector2(0, 0)
for m in (move_map[key] for key in move_map if pressed[key]):
move_vector += m
# normalize movement vector if necessary
if move_vector.length() > 0:
move_vector.normalize_ip()
# apply speed to movement vector
move_vector *= speed
# update position of player
pos += move_vector
clock.tick(60)
just use the events return data, instead of trying to poll, you're already checking if its a keydown event TYPE, now just interrogate the KEY index, like so:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
player.pos = (player.pos[0] - 2, player.pos[1])
rest of code.....
also consider using a separate data structure to store the state of your controls, then just use the events to update that data structure. That will help make the controls a bit more flexible as you wont be relying on the event queue to cause your character to move, which in my experience causes problems like: not being able to push more than two buttons at a time, and odd delay or timing issues with character movements. so something like:
keystates={'up':False, 'down':False, 'left':False, 'right':False}
running=True
#start main pygame event processing loop here
while running:
for event in pygame.event.get():
if event.type == QUIT:
running=False
#check for key down events
if event.type == KEYDOWN:
if event.key == K_UP:
keystates['up']=True
if event.key == K_DOWN:
keystates['down']=True
if event.key == K_LEFT:
keystates['left']=True
if event.key == K_RIGHT:
keystates['right']=True
#check for key up events
if event.type == KEYUP:
if event.key == K_UP:
keystates['up']=False
if event.key == K_DOWN:
keystates['down']=False
if event.key == K_LEFT:
keystates['left']=False
if event.key == K_RIGHT:
keystates['right']=False
#do something about the key states here, now that the event queue has been processed
if keystates['up']:
character.moveUp() #or whatever your call for these are...
if keystates['down']:
character.moveDown()
if keystates['left']:
character.moveLeft()
if keystates['right']:
character.moveRight()
#gracefully exit pygame here
pygame.quit()
You are using event-based input , but in this case you want polling-based input. Then you don't mess with key-repeats.
import pygame
from pygame.locals import *
done = False
player.pos = Rect(0,0,10,10)
while not done:
for event in pygame.event.get():
# any other key event input
if event.type == QUIT:
done = True
elif event.type == KEYDOWN:
if event.key == K_ESC:
done = True
elif event.key == K_F1:
print "hi world mode"
# get key current state
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
player.pos.left -= 10
if keys[K_RIGHT]:
player.pos.left += 10
if keys[K_UP]:
player.pos.top -= 10
if keys[K_DOWN]:
player.pos.left += 10
if keys[K_SPACE]:
print 'firing repeated gun'
My guess is that set repeat doesn't work the way that you think it will. Basically, after your second key goes up, the repeat doesn't happen. This would seem to make sense to me: open up a text editor and hold down the "A" key. "A"s will spill out across the screen. Then, press the "J" key with the "A" key still held down. The "A"s stop. That is a typical key repeat system.
I'm not sure using this "set_repeat" method is going to work out in the end anyway. Basically, any key that the player presses will now "repeat", even if they click "fire" or "jump".
As an alternative, try saving the state when the user presses or releases. Don't use the set_repeat, but do something like the following:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if pygame.key.get_pressed()[K_LEFT]:
player.moving_left = True
if pygame.key.get_pressed()[K_RIGHT]:
player.moving_right = True
if pygame.key.get_pressed()[K_UP]:
player.moving_up = True
if pygame.key.get_pressed()[K_DOWN]:
player.moving_down = True
elif event.type == KEYUP:
if pygame.key.get_pressed()[K_LEFT]:
player.moving_left = False
if pygame.key.get_pressed()[K_RIGHT]:
player.moving_right = False
if pygame.key.get_pressed()[K_UP]:
player.moving_up = False
if pygame.key.get_pressed()[K_DOWN]:
player.moving_down = False
# Somewhere else in your game loop...
if player.moving_left:
player.pos[0] -= 2
if player.moving_right:
player.pos[0] += 2
if player.moving_up:
player.pos[1] -= 2
if player.moving_right:
player.pos[1] += 2