4
\$\begingroup\$

I've been writing a simple top down mini RPG in python.

My problem is that when I move the player I have to repeatedly tap the arrow key. Each time I tap the key the player moves 5 PX in the direction of the key I press, but if I hold down the key he doesn't keep moving, he just moves the first 5 PX.

My moving code looks like this:

# event loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT: 
            sys.exit()        
        elif event.type == pygame.KEYDOWN:          # check for key presses          
            if event.key == pygame.K_LEFT:        # left arrow turns left
                x = x + -x_speed
            elif event.key == pygame.K_RIGHT:     # right arrow turns right
                x = x + x_speed
            elif event.key == pygame.K_UP:        # up arrow goes up
                y = y + -y_speed
            elif event.key == pygame.K_DOWN:     # down arrow goes down
                y = y + y_speed    

And farther up I define the x_speed ect.

How do I make it keep moving when I hold down the key?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can only get one KEYDOWN event per key press. If you want to hold down a key you need to toggle a boolean. I read below that you couldn't get this to work, but I'm pretty confident that it is the only way you can get the effect you are looking for. \$\endgroup\$ May 2, 2013 at 21:22

6 Answers 6

9
\$\begingroup\$

Your problem is the fact that you're only looking at KEYDOWN events.

What you need to do is toggle a boolean value when a key is pressed or released.

Something like this would work:

# event loop
for event in pygame.event.get():
    if event.type == pygame.QUIT: 
        sys.exit()        
    elif event.type == pygame.KEYDOWN:          # check for key presses          
        if event.key == pygame.K_LEFT:        # left arrow turns left
            pressed_left = True
        elif event.key == pygame.K_RIGHT:     # right arrow turns right
            pressed_right = True
        elif event.key == pygame.K_UP:        # up arrow goes up
            pressed_up = True
        elif event.key == pygame.K_DOWN:     # down arrow goes down
            pressed_down = True
    elif event.type == pygame.KEYUP:            # check for key releases
        if event.key == pygame.K_LEFT:        # left arrow turns left
            pressed_left = False
        elif event.key == pygame.K_RIGHT:     # right arrow turns right
            pressed_right = False
        elif event.key == pygame.K_UP:        # up arrow goes up
            pressed_up = False
        elif event.key == pygame.K_DOWN:     # down arrow goes down
            pressed_down = False

# In your game loop, check for key states:
if pressed_left:
    x -= x_speed
if pressed_right:
    x += x_speed
if pressed_up:
    y -= y_speed
if pressed_down:
    y += y_speed

This way, you're actually looking at both presses and releases, and moving while the key is pressed.

\$\endgroup\$
2
  • \$\begingroup\$ this didn't work \$\endgroup\$ May 2, 2013 at 15:42
  • \$\begingroup\$ Oh bugger, I didn't realize the fact that the state-checking code needs to be in your gameloop. \$\endgroup\$ May 2, 2013 at 23:08
4
\$\begingroup\$

I don't know python, but have you tried setting a bool to true when the key is pressed, and changing the speed in an if statement based on that bool. When the key gets released you just have to set the bool back to false.

\$\endgroup\$
3
  • \$\begingroup\$ Ok I'll try that \$\endgroup\$ May 1, 2013 at 18:54
  • 1
    \$\begingroup\$ If the API doesn't provide an "is pressed" state for the keys, that is a good solution. \$\endgroup\$
    – Cristina
    May 1, 2013 at 18:58
  • \$\begingroup\$ I couldn't get this to work. \$\endgroup\$ May 2, 2013 at 15:41
3
\$\begingroup\$

I don't know python or pygame, but assuming you're using a game library there should be a way to poll the state of the key, such as if it's currently down or not instead of if it was pressed since last update. Use that for checking and updating movement.

The next problem you will run into is it will update movement as fast as your logic update interval is as long as the key is held down. To fix this you need a variable to act as a cooldown so when you poll the state of the key and use it to move, the variable is set to the cooldown amount, and you can't move again until that cooldown reaches zero. So for an example of basic movement code assuming an arbitrary library and language:

constant int COOLDOWN_TIME = 170; //170 milliseconds
int cooldown = 0;

void update(int delta) {
    if (cooldown <= 0) {
        if (Input.isKeyDown(Key_Left)) {
            x -= x_speed;
            cooldown += COOLDOWN_TIME;
        }
        else if (Input.isKeyDown(Key_Right)) {
            x += x_speed;
            cooldown += COOLDOWN_TIME;
        }
        if (Input.isKeyDown(Key_Up)) {
            y -= y_speed;
            cooldown += COOLDOWN_TIME;
        }
        else if (Input.isKeyDown(Key_Down)) {
            y += y_speed
            cooldown += COOLDOWN_TIME;
        }
    }
    else {
        cooldown -= delta;
    }
}

Where delta would be the time in milliseconds passed since the last logic update.

\$\endgroup\$
2
\$\begingroup\$

I asked a question here that's very similar, and received the following answer:

The way to deal with this is to set a timer once the person taps the phone. The most user friendly scenario that you'd implement would look something like this:

  1. When you detect a tap, set a timer (t = timeToRepeat)

  2. On each frame, decrease the timer by dt

  3. If the timer reaches zero, move the sprite a tile and reset the timer
  4. If the user releases their finger before the first cycle of the timer, move the sprite one tile

Obviously the amount of time that you set your timer to will determine how fast your sprite moves. There are a few variations on this theme depending on the kind of behavior you want as well. For example, you can move the sprite once immediately upon detecting a tap and ignore step #4.

Implementing this would make the user able to tap to move a single time and to be able to move multiple tiles by holding, which I think is ideal.

\$\endgroup\$
2
\$\begingroup\$

I believe that this is what you were looking for:

keys = pygame.key.get_pressed()  # checking pressed keys
    if keys[pygame.K_ESCAPE]:  # If 'esc' - QUIT GAME
        game_exit = True
    if keys[pygame.K_RIGHT]:  # If 'right' - MOVE RIGHT
        character.move('right')

The longer you hold the key down, the more it would move

\$\endgroup\$
1
  • \$\begingroup\$ This works a treat, and is a heck of a lot shorter than the Answer. Much better (for me, anyway). Thanks. \$\endgroup\$
    – Jayce
    Mar 3, 2020 at 17:59
1
\$\begingroup\$

try to make a variable that holds up your movement results and then put the variable in the while loop not the for loop something like that

GameExit = True
    while not GameExit
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                GameExit = 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

x is your object x place

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .