Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Pygame diagonal movement not at correct angle

I would like my player to move diagonally at the same speed as it does when only moving horizontally or vertically. This does work when moving in a line with a negative gradient however, when moving along a positive gradient, the player doesn’t move at a perfect 45 degree angle.

This is my code for moving the player:

def move(self, speed):
    if self.direction.magnitude() != 0:
        self.direction = self.direction.normalize()
            
    self.rect.x += self.direction.x * speed
    self.rect.y += self.direction.y * speed

As I said earlier, all I want is for the player to move diagonally at the same speed as it does in only the x or y direction.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Here is the full player class in case it is needed:

class Player:
    def __init__(self, x, y):
        self.image = pygame.Surface((30, 30))
        self.image.fill((255, 255, 255))
        self.rect = self.image.get_rect(center = (x, y))
        self.direction = pygame.math.Vector2()
        self.speed = 5 

    def input(self):
        keys = pygame.key.get_pressed()

        if keys[K_w]:
            self.direction.y = -1
        elif keys[K_s]:
            self.direction.y = 1
        else:
            self.direction.y = 0
            
        if keys[K_a]:
            self.direction.x = -1
        elif keys[K_d]:
            self.direction.x = 1
        else:
            self.direction.x = 0

    def move(self, speed):
        if self.direction.magnitude() != 0:
            self.direction = self.direction.normalize()
            
        self.rect.x += self.direction.x * speed
        self.rect.y += self.direction.y * speed

    def update(self):
        self.input()
        self.move(self.speed)
            
    def draw(self, screen):
        screen.blit(self.image, self.rect.center)

>Solution :

Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.

The coordinates for Rect objects are all integers. […]

The fraction part of the coordinates gets lost when the new position of the object is assigned to the Rect object. If you want to move the object with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location of the rectangle:

class Player:
   def __init__(self, x, y):
       self.image = pygame.Surface((30, 30))
       self.image.fill((255, 255, 255))
       self.rect = self.image.get_rect(center = (x, y))
       self.direction = pygame.math.Vector2()
       self.speed = 5 

       self.position = pygame.math.Vector2(x, y)

   def input(self):
       keys = pygame.key.get_pressed()
       dx = keys[K_d] - keys[K_a]
       dy = keys[K_s] - keys[K_w]
       self.direction = pygame.math.Vector2(dx, dy)
       if dx != 0 and dy != 0:
           self.direction /= 1.41421

   def move(self, speed):    
       self.position += self.direction * speed
       self.rect.x = round(self.position.x)
       self.rect.y = round(self.position.y)
       
   def update(self):
       self.input()
       self.move(self.speed)
           
   def draw(self, screen):
       screen.blit(self.image, self.rect.center)

See also Pygame doesn’t let me use float for rect.move, but I need it and moving with a normalized vector in pygame inconsistent?.


Note, that pygame.math.Vector2.magnitude and math.Vector2.normalize are expensive for performance. Try to avoid this operations. Normalization of the vector is only required when moving along both axes. Since the absolute value of the vector is √2 in this case, the normalization can be replaced by dividing by √2.

dx = keys[K_d] - keys[K_a]                   # dx is -1, 0 or 1
dy = keys[K_s] - keys[K_w]                   # dy is -1, 0 or 1
self.direction = pygame.math.Vector2(dx, dy)
if dx != 0 and dy != 0:  
    self.direction /= 1.41421                # sqrt(2) is ~1.41421
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading