Reputation: 138
I want to implement smooth motion that accelerates up to max speed and then decelerates slowly if no keys are pressed, similar to the motion of the space ship in asteroid. Here is my current code for the movement:
import pygame
pygame.init()
display = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
GRAY = pygame.Color('gray12')
display_width, display_height = display.get_size()
x = display_width * 0.45
y = display_height * 0.8
x_change = 0
y_change = 0
accel_x = 0
accel_y = 0
max_speed = 6
sign = lambda a: (a > 0) - (a < 0)
crashed = False
while not crashed:
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
# Set the acceleration value.
if event.key == pygame.K_LEFT:
print('left')
accel_x = -.2
if event.key == pygame.K_RIGHT:
print('right')
accel_x = .2
if event.key == pygame.K_DOWN:
print('down')
accel_y = .2
if event.key == pygame.K_UP:
print('up')
accel_y = -.2
if event.type == pygame.KEYUP:
if event.key in (pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN):
print('key up')
accel_x = 0
accel_y = 0
x_change += accel_x # Accelerate.
y_change += accel_y
if abs(x_change) >= max_speed: # If max_speed is exceeded.
# Normalize the x_change and multiply it with the max_speed.
x_change = sign(x_change) * max_speed
if abs(y_change) >= max_speed: # If max_speed is exceeded.
# Normalize the y_change and multiply it with the max_speed.
y_change = sign(y_change) * max_speed
# Decelerate if no key is pressed.
if accel_x == 0:
x_change *= 0.98
if accel_y == 0:
y_change *= 0.98
x += x_change # Move the object.
y += y_change
display.fill(GRAY)
pygame.draw.rect(display, (0, 120, 250), (x, y, 20, 40))
pygame.display.update()
clock.tick(60)
pygame.quit()
When pressing the arrow keys individually the motion works perfectly, however if pressing 2 keys in rapid succession, the second key is registered but there is no motion from the object. I've tried making a list of keys using pygame.key.get_pressed()
and comparing the current motion with the key pressed in the list, however this didn't solve the problem.
Upvotes: 2
Views: 822
Reputation: 210909
The major issue in your code is, that when you press and LEFT, then press and hold RIGHT and finally release LEFT, the resulting states of accel_x
and accel_y
are both 0, because the last event that happend was pygame.KEYUP
.
I recommend to evaluate the states of pygame.key.get_pressed()
rather than to use the key events:
y_change
up to its maximum (y_change = max(y_change-0.2, -max_speed)
). y_change
down to its minimum (y_change = min(y_change+0.2, max_speed)
). y_change *= 0.98
). Apply the same principle to x_change
, when the keys LEFT and/or RIGHT are pressed:
crashed = False
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
keys = pygame.key.get_pressed()
# handle left and right movement
if keys[pygame.K_LEFT] and not keys[pygame.K_RIGHT]:
x_change = max(x_change-0.2, -max_speed)
elif keys[pygame.K_RIGHT] and not keys[pygame.K_LEFT]:
x_change = min(x_change+0.2, max_speed)
else:
x_change *= 0.98
# handle up and down movement
if keys[pygame.K_UP] and not keys[pygame.K_DOWN]:
y_change = max(y_change-0.2, -max_speed)
elif keys[pygame.K_DOWN] and not keys[pygame.K_UP]:
y_change = min(y_change+0.2, max_speed)
else:
y_change *= 0.98
x += x_change # Move the object.
y += y_change
display.fill(GRAY)
pygame.draw.rect(display, (0, 120, 250), (x, y, 20, 40))
pygame.display.update()
clock.tick(60)
Upvotes: 1