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.