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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented May 1, 2013 at 18:58
  • \$\begingroup\$ I couldn't get this to work. \$\endgroup\$ Commented 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\$ Commented 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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.