1

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

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
JustOscarJ
  • 99
  • 1
  • 8
  • Please [edit] your question with a [mcve]. In particular, be sure to include the `class` line which contains the two methods and show how all variables are initialized. – Code-Apprentice Jun 11 '22 at 05:24
  • Your `calculate_new_xy()` is mathematically correct. However, there are quirks of floating point arithmetic that might be causing the issue you see here. I suggest debugging your code to find out what it is doing. Do a few calculations by hand to get the values you expect for a specific rotation that isn't working right and compare with the results your code gives. [This article](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) has some great tips to get you started if you are unfamiliar with debugging techniques. – Code-Apprentice Jun 11 '22 at 05:28
  • Here's a handy python trick: `self.x, self.y = calculate_new_xy(...)` is the same as the 3 lines you have here. – Code-Apprentice Jun 11 '22 at 05:29
  • Hmm. Ok. Is the looping rotation stuff correct? Should it be -180 to +180? – JustOscarJ Jun 11 '22 at 05:30
  • What looping? There is no loop in the code you show here. – Code-Apprentice Jun 11 '22 at 05:30
  • I meant wrapping, I guess. The "if self.rotation == 181:" ... – JustOscarJ Jun 11 '22 at 05:31
  • I don't see anything obviously wrong. As I said, you will need to debug your code with `print()` statements or a visual debugger to figure out what's going on here. – Code-Apprentice Jun 11 '22 at 05:32
  • 1
    See [Image rotation while moving](https://stackoverflow.com/questions/57226587/image-rotation-while-moving/57227063#57227063), [Move Character with Vector](https://stackoverflow.com/questions/65688244/move-character-with-vector/65688573#65688573) and [How to turn the sprite in pygame while moving with the keys](https://stackoverflow.com/questions/64792467/how-to-turn-the-sprite-in-pygame-while-moving-with-the-keys/64792568#64792568) and – Rabbid76 Jun 11 '22 at 05:33
  • Oh...the 181 changing to -180 is a little weird. Maybe it should be -179. – Code-Apprentice Jun 11 '22 at 05:33

1 Answers1

1

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)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174