So I'm using pygame to create a simple top-down shooter game, and I'm doing lots of angle calculations from the top-down perspective. Let's use a simple arrow and a ball as an example, I want the red arrow to keep pointing at the blue ball regardless of where the blue ball moves:
And it seemed easy enough, I just needed atan2:
angle = math.atan2(blue.y - red.y, blue.x - red.x)
But the problem is, atan2 works for a mathematical coordinate grid like this:
Where alpha = math.atan2(blue.y - red.y, blue.x - red.x)
But the thing with pygame (on Windows at least) is that the coordinate grid doesn't work like a mathematical coordinate grid, it's actually upside down starting from the left top corner of the game window:
So while it looks like the blue ball is higher up and thus mathematically blue.y should be larger than red.y, this is actually not the case due to the upside down coordinate grid, which Python's math.atan2() doesn't know of, and the original calculation I had:
angle = math.atan2(blue.y - red.y, blue.x - red.x)
Actually yields the correct angle's negation.
Now the obvious first solution that I came up with was to just flip the sign, and fair enough it worked with this:
angle = -math.atan2(blue.y - red.y, blue.x - red.x)
But the issues started again once I needed to do further calculations based on the previously calculated angle, which technically is now upside down.
What countermeasures could I take to "permanently" get rid of this issue?
Here's an actual example of where I need this, I have a "zombie" entity which does nothing but follows the target it has been given:
class Zombie(Entity):
def __init__(self, *args, target=None, **kwargs):
super().__init__(*args, **kwargs)
self.target = target
def update(self, dt, app):
if self.target:
# Face towards target
dx = self.target.x - self.x
dy = self.target.y - self.y
self.angle = math.atan2(dy, dx)
# Change velocity towards target
speed = self.get_max_speed()
vel_x = math.cos(angle) * speed
vel_y = math.sin(angle) * speed
self.velocity = (vel_x, vel_y)
else:
self.velocity = (0, 0)
# Moves the zombie based on velocity
super().update(dt, app)
For this particular case I managed to solve it by storing the angle into a separate variable for later use, and negating it separately upon setting the self.angle:
# Face towards target
dx = self.target.x - self.x
dy = self.target.y - self.y
angle = math.atan2(dy, dx)
self.angle = -angle
# Change velocity towards target
speed = self.get_max_speed()
vel_x = math.cos(angle) * speed
vel_y = math.sin(angle) * speed
self.velocity = (vel_x, vel_y)
But this is just begging for more bugs, and I'm looking for a more generic solution to the issue.



