I have this simple script for movement.
if x > 0:
if key[pygame.K_a] or key[pygame.K_LEFT]:
rect_player.move_ip(-1 * speed, 0)
if x < SCREEN_WIDTH - 110:
if key[pygame.K_d] or key[pygame.K_RIGHT]:
rect_player.move_ip(speed, 0)
if y > 0:
if key[pygame.K_w] or key[pygame.K_UP]:
rect_player.move_ip(0, -1 * speed)
if y < SCREEN_HEIGHT - 110:
if key[pygame.K_s] or key[pygame.K_DOWN]:
rect_player.move_ip(0, speed)
When the player travels in one direction, everything goes fine. But when they travel diagonally, it goes faster. Is there any way to fix this?
Since pygame.Rect
is supposed to represent an area on the screen, a pygame.Rect
object can only store integral data (the decimal places are lost when you assign a floating point number).
The coordinates for Rect objects are all integers. [...]
I recommend to use pygame.math.Vector2
for the problem. Store the position of the object in a pygame.math.Vector2
object:
pos = pygame.math.Vector2(start_x, start_y)
Set a direction vector depending on the keys. Scale the vector to the length of speed
with scale_to_length
. Move the object and update rect_player
:
key = pygame.key.get_pressed()
up = key[pygame.K_w] or key[pygame.K_UP]
down = key[pygame.K_s] or key[pygame.K_DOWN]
left = key[pygame.K_a] or key[pygame.K_LEFT]
right = key[pygame.K_d] or key[pygame.K_RIGHT]
move = pygame.math.Vector2(right - left, down - up)
if move.length_squared() > 0:
move.scale_to_length(speed)
pos += move
rect_player.topleft = round(pos.x), round(pos.y)
If you don't care about the floating point accuracy just move the rectangle:
key = pygame.key.get_pressed()
up = key[pygame.K_w] or key[pygame.K_UP]
down = key[pygame.K_s] or key[pygame.K_DOWN]
left = key[pygame.K_a] or key[pygame.K_LEFT]
right = key[pygame.K_d] or key[pygame.K_RIGHT]
move = pygame.math.Vector2(right - left, down - up)
if move.length_squared() > 0:
move.scale_to_length(speed)
rect_player.move_ip(round(move.x), round(move.y))
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
rect_player = pygame.Rect(0, 0, 20, 20)
rect_player.center = window.get_rect().center
speed = 5
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
key = pygame.key.get_pressed()
up = key[pygame.K_w] or key[pygame.K_UP]
down = key[pygame.K_s] or key[pygame.K_DOWN]
left = key[pygame.K_a] or key[pygame.K_LEFT]
right = key[pygame.K_d] or key[pygame.K_RIGHT]
move = pygame.math.Vector2(right - left, down - up)
if move.length_squared() > 0:
move.scale_to_length(speed)
rect_player.move_ip(round(move.x), round(move.y))
window.fill(0)
pygame.draw.rect(window, (255, 0, 0), rect_player)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
If you derive the velocity by math.sqrt(2)
but continue to use a pygame.Rect
, it still does not lead to an accurate movement. pygame.Rect objects (rect_player
) can only store integral values (the decimal places are lost when you assign a floating point number). Therefore this works only approximately and fails completely if seed is a small value (e.g. 1). The only value for which the solution works really well is for speed=10
because 10/math.sqrt(2)
, is 7.071 (~7).