简体   繁体   中英

Finding proper X/Y coordinate modifiers with given angle in Python/Pygame

I am trying to make a sprite move directly towards the mouse, utilizing the angle between them. This angle is found via the atan2 function. While this angle works fine for rotating the sprite towards the mouse, the sprite moves in the wrong directions depending on the quadrant of the given angle. It will sometimes freeze up in one quadrant, or move directly opposite the mouse.

I am using basic Trig functions to find the angle, and calculate proper additions to the X and Y variables of the sprite. It is also important to note that the angle I calculate, while it doesn't work for movement, does work perfectly for rotation. What's odd is that I pass the X-difference between the two spots, and THEN the Y-difference, which is the opposite of how the inverse tangent function is supposed to be handled. Therefore, I'm not even sure how this angle has been making rotation work correctly.

I've attempted to pass the Y-difference and the X-difference (in that order) into the atan2 function. However, this causes the rotation on my sprite to be wrong, pointing me towards the idea that the angle as a whole is also incorrect. I've also tried following along with numerous other programs, all of which use the same formulas as me. However, these don't work, even when I change the order of the arguments to the atan2 function to match the example programs.

def main():

ExitLoop = False

image = IMAGELOADER.AllImages["Fighter1"]
image2 = IMAGELOADER.AllImages["Fighter2"]
Fighter1 = FighterClass.Fighter(image,(700,700))
Fighter2 = FighterClass.Fighter(image2,(300,300))
while not ExitLoop:

    ScreenController.Refresh()
    mouse_pos = pygame.mouse.get_pos()
    Fighter2.set_x(mouse_pos[0]-32)
    Fighter2.set_y(mouse_pos[1]-32)
    angle = math.atan2(Fighter1.get_x()-mouse_pos[0]+32, Fighter1.get_y()-mouse_pos[1]+32)
    degrees_angle = math.degrees(angle)
    Fighter1.rotate(degrees_angle)
    xval = Fighter1.get_x()
    yval = Fighter1.get_y()
    speed = Fighter1.get_speed()
    changex = (speed*math.cos(angle))
    changey = (speed*math.sin(angle))
    Fighter1.set_x(xval+changex)
    Fighter1.set_y(yval+changey)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            ExitLoop = True
    ScreenController.Draw(Fighter1.get_image(),Fighter1.get_rect(),False)
    ScreenController.Draw(Fighter2.get_image(),Fighter2.get_rect(),False)
    ScreenController.DisplayUpdate()
    clock.tick(60)

Class Code (Relevant to the fighter class)

import pygame
import WoodysFunctions
class Fighter(pygame.sprite.Sprite):
    def __init__(self,image,XnY):
        pygame.sprite.Sprite.__init__(self)
        self.image = image
        self.__image_source = image
        self.rect = self.image.get_rect()
        self.__mask = pygame.mask.from_surface(self.image)
        self.rect.x = XnY[0]
        self.rect.y = XnY[1]
        self.__speed = 1
    def get_image(self):
        return self.image
    def get_rect(self):
        return self.rect
    def get_mask(self):
        return self.__mask
    def get_x(self):
        return self.rect.x
    def get_y(self):
        return self.rect.y
    def get_speed(self):
        return self.__speed

    def set_image(self,value):
        self.image = value
    def set_rect(self,value):
        self.__rect = value
    def set_mask(self,value):
        self.__mask = value
    def set_x(self,value):
        self.rect.x = value
    def set_y(self,value):
        self.rect.y = value
    def set_speed(self,value):
        self.__speed = value

    def rotate(self,angle):
        old_center = self.rect.center
        self.image = pygame.transform.rotate(self.__image_source,angle)
        self.rect = self.image.get_rect()
        self.rect.center = old_center

Expected output: Sprite moves straight towards the mouse

Actual behavior: Sprite moves in wrong directions, with behavior showing patterns depending on quadrant of calculated angle.

Edit: I changed the program so that the X and Y variables of the sprite are stored in variables separate from the rect object. This prevents decimal truncation. I also recalculated the angle between the sprite and the mouse pointer after the rotation code is finished. In the recalculation, the X and Y difference parameters are swapped to match the inverse tangent function instead of the inverse cotangent function. This recalculated angle is used for angular movement, and the first angle, with the X difference passed first, is used for rotation. It is important to note that after I calculated the changeX and changeY variables using the recalculated angle (with the Y difference passed first), I multiplied them by -1, as otherwise the sprite will move away from the mouse pointer.

I cannot be 100% sure, but I think the problem is that pygame.Rect stores position as integers, because it's supposed to store coordinates and dimensions in pixel unit, and of course you cannot paint half pixel.

Since you are dealing with any angle and trigonometric functions, you end with floats which are truncated when you do:

def set_x(self,value):
    self.rect.x = value

Here, if value is 1.4, self.rect.x becomes 1. So you lose "accuracy."

This loss of accuracy is propagated each iteration of the main loop (each frame), resulting in an unexpected motion direction.

The best solution is to store all your value in a separate data structure and update the rect attribute only for drawing in the screen.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM