# RPG movement holding down button

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?

• 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. May 2 '13 at 21:22

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.

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

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.

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

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.

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.

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

• This works a treat, and is a heck of a lot shorter than the Answer. Much better (for me, anyway). Thanks. Mar 3 '20 at 17:59

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