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.