77

I am making a game in pygame 1.9.2. It's a faily simple game in which a ship moves between five columns of bad guys who attack by moving slowly downward. I am attempting to make it so that the ship moves left and right with the left and right arrow keys. Here is my code:

keys=pygame.key.get_pressed()
if keys[K_LEFT]:
    location-=1
    if location==-1:
        location=0
if keys[K_RIGHT]:
    location+=1
    if location==5:
        location=4

It works too well. The ship moves too fast. It is near impossible to have it move only one location, left or right. How can i make it so the ship only moves once every time the key is pressed?

Shaido
  • 27,497
  • 23
  • 70
  • 73
Dan G.
  • 982
  • 1
  • 8
  • 20

12 Answers12

116

You can get the events from pygame and then watch out for the KEYDOWN event, instead of looking at the keys returned by get_pressed()(which gives you keys that are currently pressed down, whereas the KEYDOWN event shows you which keys were pressed down on that frame).

What's happening with your code right now is that if your game is rendering at 30fps, and you hold down the left arrow key for half a second, you're updating the location 15 times.

events = pygame.event.get()
for event in events:
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_LEFT:
            location -= 1
        if event.key == pygame.K_RIGHT:
            location += 1

To support continuous movement while a key is being held down, you would have to establish some sort of limitation, either based on a forced maximum frame rate of the game loop or by a counter which only allows you to move every so many ticks of the loop.

move_ticker = 0
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
    if move_ticker == 0:
        move_ticker = 10
        location -= 1
        if location == -1:
            location = 0
if keys[K_RIGHT]:
    if move_ticker == 0:   
        move_ticker = 10     
        location+=1
        if location == 5:
            location = 4

Then somewhere during the game loop you would do something like this:

if move_ticker > 0:
    move_ticker -= 1

This would only let you move once every 10 frames (so if you move, the ticker gets set to 10, and after 10 frames it will allow you to move again)

kabr8
  • 341
  • 1
  • 2
  • 19
Haz
  • 2,539
  • 1
  • 18
  • 20
  • 1
    I tried to use this but the problem was that it only moved if I pushed the button at the exact moment pygame was looking for that event. It wouldn't move when I wanted it to. – Dan G. Apr 16 '13 at 18:26
  • 2
    Well you have to inspect your events on each update of your game loop. Were you doing this? Events will stay in Pygame's event queue until you read them or pump them. – Haz Apr 16 '13 at 18:27
  • 2
    Dan, this answer is exactly what you asked for, but keep in mind that using events you won't be able to continuously move your ship that you might want if you are able to control the speed. You could keep you original code and once you detect a key pressed, you can easily ignore the pressed buttons for N loops. – pmoleri Apr 16 '13 at 18:29
  • 2
    Dan, I've also updated my answer to show you a way that you can hold a key down and still manage how quickly the player moves. – Haz Apr 16 '13 at 18:40
  • I implemented a limitation on the places it can move, but my game window is small enough (only has 5 locations) that i need no repetition. If i plan to make it bigger I'll keep you in mind. – Dan G. Apr 16 '13 at 18:43
18

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 1, otherwise 0. It is a snapshot of the keys at that very moment The new state of the keys must be retrieved continuously in each frame. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement:

while True:

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        x -= speed
    if keys[pygame.K_RIGHT]:
        x += speed
    if keys[pygame.K_UP]:
        y -= speed
    if keys[pygame.K_DOWN]:
        y += speed

This code can be simplified by subtracting "left" from "right" and "up" from "down":

while True:

    keys = pygame.key.get_pressed()
    x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * speed
    y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * speed

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 movement:

while True:

    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                x -= speed
            if event.key == pygame.K_RIGHT:
                x += speed
            if event.key == pygame.K_UP:
                y -= speed
            if event.key == pygame.K_DOWN:
                y += speed

See also Key and Keyboard event


Minimal example of continuous movement: replit.com/@Rabbid76/PyGame-ContinuousMovement

import pygame

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

rect = pygame.Rect(0, 0, 20, 20)
rect.center = window.get_rect().center
vel = 5

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            print(pygame.key.name(event.key))

    keys = pygame.key.get_pressed()
    
    rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel
    rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * vel
        
    rect.centerx = rect.centerx % window.get_width()
    rect.centery = rect.centery % window.get_height()

    window.fill(0)
    pygame.draw.rect(window, (255, 0, 0), rect)
    pygame.display.flip()

pygame.quit()
exit()

Minimal example for a single action: replit.com/@Rabbid76/PyGame-ShootBullet

import pygame
pygame.init()

window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()

tank_surf = pygame.Surface((60, 40), pygame.SRCALPHA)
pygame.draw.rect(tank_surf, (0, 96, 0), (0, 00, 50, 40))
pygame.draw.rect(tank_surf, (0, 128, 0), (10, 10, 30, 20))
pygame.draw.rect(tank_surf, (32, 32, 96), (20, 16, 40, 8))
tank_rect = tank_surf.get_rect(midleft = (20, window.get_height() // 2))

bullet_surf = pygame.Surface((10, 10), pygame.SRCALPHA)
pygame.draw.circle(bullet_surf, (64, 64, 62), bullet_surf.get_rect().center, bullet_surf.get_width() // 2)
bullet_list = []

run = True
while run:
    clock.tick(60)
    current_time = pygame.time.get_ticks()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

        if event.type == pygame.KEYDOWN:
            bullet_list.insert(0, tank_rect.midright)

    for i, bullet_pos in enumerate(bullet_list):
        bullet_list[i] = bullet_pos[0] + 5, bullet_pos[1]
        if bullet_surf.get_rect(center = bullet_pos).left > window.get_width():
            del bullet_list[i:]
            break

    window.fill((224, 192, 160))
    window.blit(tank_surf, tank_rect)
    for bullet_pos in bullet_list:
        window.blit(bullet_surf, bullet_surf.get_rect(center = bullet_pos))
    pygame.display.flip()

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
12
import pygame
pygame.init()
pygame.display.set_mode()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit(); #sys.exit() if sys is imported
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_0:
                print("Hey, you pressed the key, '0'!")
            if event.key == pygame.K_1:
                print("Doing whatever")

In note that K_0 and K_1 aren't the only keys, to see all of them, see pygame documentation, otherwise, hit tab after typing in

pygame.

(note the . after pygame) into an idle program. Note that the K must be capital. Also note that if you don't give pygame a display size (pass no args), then it will auto-use the size of the computer screen/monitor. Happy coding!

kabr8
  • 341
  • 1
  • 2
  • 19
PMARINA
  • 304
  • 4
  • 17
2

I think you can use:

pygame.time.delay(delayTime)

in which delayTime is in milliseconds.

Put it before events.

סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Erfaan
  • 21
  • 1
1

Try this:

keys=pygame.key.get_pressed()
if keys[K_LEFT]:
    if count == 10:
        location-=1
        count=0
    else:
        count +=1
    if location==-1:
        location=0
if keys[K_RIGHT]:
    if count == 10:
        location+=1
        count=0
    else:
        count +=1
    if location==5:
        location=4

This will mean you only move 1/10 of the time. If it still moves to fast you could try increasing the value you set "count" too.

Samuel
  • 53
  • 8
1

The reason behind this is that the pygame window operates at 60 fps (frames per second) and when you press the key for just like 1 sec it updates 60 frames as per the loop of the event block.

clock = pygame.time.Clock()
flag = true
while flag :
    clock.tick(60)

Note that if you have animation in your project then the number of images will define the number of values in tick(). Let's say you have a character and it requires 20 sets images for walking and jumping then you have to make tick(20) to move the character the right way.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Ansh
  • 58
  • 6
0

Just fyi, if you're trying to ensure the ship doesn't go off of the screen with

location-=1
if location==-1:
    location=0

you can probably better use

location -= 1
location = max(0, location)

This way if it skips -1 your program doesn't break

Nathan
  • 3,558
  • 1
  • 18
  • 38
0

make something like this, but based on time delay. i call my function first time immediately and then lunch timer, and while button is pressed i call it every button_press_delta seconds

from time import time
before main loop:
button_press_delta = 0.2
right_button_pressed = 0
while not done:
    keys = pygame.key.get_pressed()
    if keys[pygame.K_RIGHT]:
        if not right_button_pressed:
            call_my_function()
            right_button_pressed = 1
            right_button_pressed_time_start = time()
        if right_button_pressed:
            right_button_pressed_time = (
                time() - right_button_pressed_time_start)
            if right_button_pressed_time > button_press_delta:
                call_my_function()
                right_button_pressed_time_start = time()
    else:
        right_button_pressed = 0
Ditya
  • 1
  • 1
0

Pygames tick function (clock.tick(5)), returns the time since last frame (deltatime). You can multiply ALL movement by this, so that the character moves at the same speed no matter the framerate. You will also need to multiply it again by a number such as 50 so it isn't too slow.

Hchap
  • 58
  • 6
-2

You should use clock.tick(10) as stated in the docs.

BoJack Horseman
  • 4,406
  • 13
  • 38
  • 70
PyGuy
  • 49
  • 3
-3

all of the answers above are too complexicated i would just change the variables by 0.1 instead of 1 this makes the ship 10 times slower if that is still too fast change the variables by 0.01 this makes the ship 100 times slower try this

keys=pygame.key.get_pressed()
if keys[K_LEFT]:
    location -= 0.1 #or 0.01
    if location==-1:
    location=0
if keys[K_RIGHT]:
    location += 0.1 #or 0.01
    if location==5:
        location=4
pi guy
  • 106
  • 1
  • 3
  • 13
  • 1
    hey, this would work except for the fact that all movements must be integer values. getting into rounding is unnessecary as well. Using the pygame.KEYDOWN event was the solution i was looking for, one movement per button press. It works well when there are only 5 possible locations. – Dan G. Aug 03 '16 at 21:43
-3

To slow down your game, use pygame.clock.tick(10)

Mr Python
  • 89
  • 1
  • 5