How do I move a character based on it's rotation?

Issue

I have a character in PyGame which I want to move forward depending on its rotation. Currently it’s very strange and wonky.

Here’s all my code: https://www.toptal.com/developers/hastebin/enapuravet.lua

Here’s the relevent code:

    def rotate(self, amount):
        self.rotation += amount
        if self.rotation == 181:
            self.rotation = -180
        elif self.rotation == -181:
            self.rotation = 180
        print(self.rotation)
        self.capybara = pygame.transform.rotate(self.base_capybara, self.rotation)
        self.rect = self.capybara.get_rect(center = self.rect.center)

    def move_forward(self, speed):
        print(self.rotation)
        new_coordinates = calculate_new_xy((self.x, self.y), 2, self.rotation*(math.pi/180))
        self.x = new_coordinates[0]
        self.y = new_coordinates[1]
        self.rect = self.capybara.get_rect(center = (self.x, self.y))

def calculate_new_xy(old_xy,speed,angle_in_radians):
    print(angle_in_radians)
    new_x = old_xy[0] + (speed*math.cos(angle_in_radians))
    new_y = old_xy[1] + (speed*math.sin(angle_in_radians))
    return new_x, new_y

Here’s what it does:
https://gyazo.com/3b7bf1c5b2e760a53913b6d3acae6e67

I also tried some other x_y calculating functions like this:

    move_vec = pygame.math.Vector2()
move_vec.from_polar((speed, angle_in_degrees))
return old_xy + move_vec

but they had the same/very similar results.

This is the capybara:

capybara

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. […]

If you want to store object positions 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 (e.g. .center) of the rectangle.

The unit of the angle of math.sin and math.cos is Randian. You needs to convert the angle from degrees to radians with math.radians:

class Capybara:
    def __init__(self, size_multiplier):
        # [...]

        self.x, self.y = 300, 300
        self.rect = self.capybara.get_rect(center = (round(self.x), round(self.y)))

    def rotate(self, amount):
        self.rotation += amount
        ifv self.rotation > 360:
            self.rotation -= 360
        elif self.rotation < 0:
            self.rotation += 360
        self.capybara = pygame.transform.rotate(self.base_capybara, self.rotation)
        self.rect = self.capybara.get_rect(center = (round(self.x), round(self.y)))
        
    def move(self, move):
        self.x += move * math.cos(math.radians(self.rotation + 90))
        self.y -= move * math.sin(math.radians(self.rotation + 90))

See also Move Character with Vector, Image rotation while moving or How to turn the sprite in pygame while moving with the keys.


Minimal example:

import pygame, math
pygame.init()

size = width, height = 600, 600
green = 50, 168, 82

screen = pygame.display.set_mode(size)

class Capybara:
    def __init__(self, size_multiplier):
        self.capybara = pygame.image.load('capybara.png')
        self.o_size = 92, 206
        self.new_size = (self.o_size[0] * size_multiplier,self.o_size[1] * size_multiplier)
        self.capybara = pygame.transform.scale(self.capybara, self.new_size)
        self.base_capybara = self.capybara
        self.rotation = 0
        self.x, self.y = 300, 300
        self.rect = self.capybara.get_rect(center = (round(self.x), round(self.y)))

    def rotate(self, amount):
        self.rotation += amount
        if self.rotation > 360:
            self.rotation -= 360
        elif self.rotation < 0:
            self.rotation += 360
        self.capybara = pygame.transform.rotate(self.base_capybara, self.rotation)
        self.rect = self.capybara.get_rect(center = (round(self.x), round(self.y)))
        
    def move(self, move):
        self.x += move * math.cos(math.radians(self.rotation + 90))
        self.y -= move * math.sin(math.radians(self.rotation + 90))


capybara = Capybara(0.8)

fpsClock = pygame.time.Clock()

rotate_l = False
rotate_r = False

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

            quit()

    keys = pygame.key.get_pressed()
    rotate = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]
    move = keys[pygame.K_DOWN] - keys[pygame.K_UP]
    capybara.move(-move * 5)
    capybara.rotate(-rotate)

    screen.fill(green)
    screen.blit(capybara.capybara, capybara.rect)
    pygame.display.update()
    fpsClock.tick(60)

Answered By – Rabbid76

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published