0

I'm making a game in a custom engine using C#. (Not Unity)

I've got a large grid and the x/y coordinates of two objects. The Player object and the Destination object. Along with the player's current rotation in degrees (0-360).

I've become too reliant on existing game engines and cannot work out how to find the rotation I need to put the player at to face the target.

playerRotation;//0 to 360 degrees.
playerX double = 47.43;
playerY double = 43.36;
targetX double = 52.15;
targetY double = 38.67;

My method at the moment is to try and get the distance between the objects by:

float distanceX = Math.Abs(playerX - destinationX);
float distanceY = Math.Abs(playerY - destinationY);

Which seems to work fine. Then I need to rotate the player to face the destination and have them move towards it until distanceX/Y are <= 0.

Edit: I've been messing with Atan2 to try and get the answer.

Vector2 playerCoords = new Vector2(playerX, playerY);
Vector2 targetCoords = new Vector2(targetX, targetY);

double theta = Math.Atan2((targetCoords.yValue - playerCoords.yValue), (targetCoords.xValue - playerCoords.xValue));

theta = theta * (180 / Math.PI);//Convert theta to degrees.

double sigma = playerRotation;//Direction in degrees the player is currently facing.

double omega = sigma - theta;

OutputLog("omega: " + omega);

My output log should be showing me the degrees my player needs to be facing to be facing the target. But it's giving me the wrong results.

Player: (4782, 4172) and Target: (4749, 4157)

Angle should be about 286~.

But Theta = -155 and omega = 229.

  • I see you swapped the parameters of the Atan2 function, the y-vector should be the first parameter, and the x-vector should be the second one. – AdrAs Dec 16 '20 at 18:05
  • And the angle you get might be counter-clockwise, so keep that in mind as well – AdrAs Dec 16 '20 at 18:06
  • Oh, whoops! I swapped it to be: `double theta = Math.Atan2((targetCoords.yValue - playerCoords.yValue), (targetCoords.xValue - playerCoords.xValue));` But my results are still wrong. Sometimes greater than 360 or less than 0. Is it because it's counter-clockwise? How would I go about resolving that? – Parallel Pancakes Dec 16 '20 at 18:31
  • I'm not very sure about double to int conversion. Replace "theta = theta * (180 / Math.PI)" by "theta = theta * (180.0 / Math.PI)" just to be save. (Note the .0 after 180) And you're resulting Atan2 angle is already the angle the player should look at. Therefore you can remove that sigma omega calculation. The angle you're looking for is theta, have a look at it in the output log – AdrAs Dec 16 '20 at 20:37
  • Still doesn't seem to work. Player: (4782, 4172) Target: (4749, 4157) Angle should be about 74~. But Theta = -155 and omega = 229. – Parallel Pancakes Dec 17 '20 at 22:19
  • Correction to my last post. I realized my playerRotation was counting the degrees counter clockwise. I don't think that's how it was supposed to go, so I reversed it to work clockwise (3 o clock = 90 degrees now). So the direction I should be facing in my above example should be around 286~ and not 74. – Parallel Pancakes Dec 17 '20 at 22:33
  • Atan2 is really difficult to understand. I just had a big misconception writing my testing code. See my edit below, it should clarify everything. – AdrAs Dec 18 '20 at 20:38

1 Answers1

2

Vector math can be very helpful, and it's not that complicated.

First vectors would be your player position and destination's:

Vector2 playerPos = new Vector2(playerX, playerY);
Vector2 destinationPos = new Vector2(destinationX, destinationY);

Now you can just subtract both vectors, to get a vector which points from one position to the other.

Vector2 delta = destination - playerPos; // Note, it might be the other way around: playerPos - destination

That delta vector has a length, and that is the distance between both points. There is usually a Length and a LengthSquared property available on the Vector class. Be aware however that calculating the length is quite CPU intensive because it uses a square root. If you want to compare that distance to a fixed distance like 200, just use the length squared and compare it to (200 * 200) which is way faster.

You can also use that delta, to let a bullet fly from one position to the other. You just need to normalize delta, there's a method for it, and you have it scaled down to length one. You can now use that delta, multiplied with a speed on each physics cycle to change the bullets position.

Now to get the angle, you can just use: double angle = Math.Atan2 (delta.Y, delta.X); // Note that y and x are reversed here, and it should be like that.

Note that this angle is in radians, not degrees. A circle in radians starts at -PI and ends at PI. A full circle therefore is 2 * PI. To convert radians to degrees, you can see this question

Edit

I always assume that 12 o'clock is 0 degrees, 3 o'clock is 90, 6 o'clock is 180 and 9 o'clock is 270.

But actually in the cartesian coordinate system things are a bit different. I also made this false assumption in the code below.

But it turned out that I was wrong. See this picture Cartesian angles

Now if you look at my sourcecode, all my variables are named incorrectly. If you look at their values however you can see that they match up with the picutre. Therefore the Atan2 correction is as it's supposed to be. // Using Vector2 from System.Numerics;

internal static double RadianToDegree(double rad)
{
    double thetaDegree = rad * (180.0 / Math.PI);

    // Convert negative angles into positive ones
    // https://stackoverflow.com/a/25725005/7671671
    double thetaDegree2 = (thetaDegree + 360) % 360;

    return thetaDegree2;
}

internal void Run()
{
    // Player: (4782, 4172) and Target: (4749, 4157)

    Vector2 player = new Vector2(4782, 4172);
    Vector2 target = new Vector2(4749, 4157);

    Vector2 delta = target - player;

    double theta = Math.Atan2(delta.Y, delta.X);
    double thetaDegree = RadianToDegree(theta);

    // Given cartesian coordinate system
    // positive y is up, negative is down
    // positive x is right, negative is left

    // Falsely assuming up is 0
    // Falsely assuming right is 90
    // Falsely assuming down is 180
    // Falsely assuming left is 270
    Vector2 v0 = new Vector2(0, 1);
    Vector2 v45 = new Vector2(0.5f, 0.5f);
    Vector2 v90 = new Vector2(0.5f, 0);
    Vector2 v180 = new Vector2(0, -1);
    Vector2 v270 = new Vector2(-1, 0);

    double theta0 = Math.Atan2(v0.Y, v0.X);
    double theta45 = Math.Atan2(v45.Y, v45.X);
    double theta90 = Math.Atan2(v90.Y, v90.X);
    double theta180 = Math.Atan2(v180.Y, v180.X);
    double theta270 = Math.Atan2(v270.Y, v270.X);

    double result0 = RadianToDegree(theta0);
    double result45 = RadianToDegree(theta45);
    double resultv90 = RadianToDegree(theta90);
    double resultv180 = RadianToDegree(theta180);
    double resultv270 = RadianToDegree(theta270);

    // result 0 --> 90
    // result 45 --> 45
    // result 90 --> 0
    // result 180 --> 270
    // result 270 --> 180
}
AdrAs
  • 676
  • 3
  • 14
  • This was extremely useful and led me towards a lot of productive work. But I'm still not quite there. Updating my original post with my updated work. – Parallel Pancakes Dec 16 '20 at 17:42
  • Sorry I'm confused. So you're saying Atan2 returns a result based on 0 degrees (radians?) being at the 3'o'clock position? And then it moves counter clockwise? But you said you made the false assumption in the following code. I'm kind of lost now. – Parallel Pancakes Dec 19 '20 at 19:51
  • After you converted the atan2 as I did to degrees, it starts at 0 degrees at 3'o'clock and then moves counter clockwise – AdrAs Dec 19 '20 at 22:26
  • So if my player's current rotation is being calculated starting at 12'o'clock and going counter clockwise... I only need to use the formula I already have and -90 from the atan2 result? – Parallel Pancakes Dec 20 '20 at 03:19
  • Yes, but you shouldn't do that, because it will confuse other developers. See [here](https://lehubert.com/2018/03/15/a-very-little-wisdom-about-quaternions/) – AdrAs Dec 20 '20 at 10:45
  • In an attempt to just get it to work, I tried the -90 method. But: Player: (4782, 4172) Target: (4749, 4157) Angle should be about 74~. But Theta = -155. Even if I abs that and -90 it wouldn't be correct. I feel like something is just way off somewhere. Not to mention if I change the player coordinates it gets even more or less off. – Parallel Pancakes Dec 20 '20 at 18:23
  • Okay so I've been messing with this and currently I'm at the point where I always get results... But they're anywhere from 0 to 15 degrees off. I think it has something to do with the radians to degrees but I'm not sure. – Parallel Pancakes Dec 20 '20 at 19:11
  • Theta should be never less than 0 if you also use `double thetaDegree2 = (thetaDegree + 360) % 360;` like I did. Just wondering: How do you know the angles are 0 to 15 degrees off? – AdrAs Dec 21 '20 at 11:36
  • Because the player direction is in a perfect 12'o'clock world and I manually set the coordinates. Even at perfect 45/90 angles the formula is usually off by a bit. – Parallel Pancakes Dec 21 '20 at 17:41
  • How far are your objects apart? I'm wondering if increasing the distance improves the accuracy – AdrAs Dec 23 '20 at 18:32
  • I find the closer it gets, the more accurate it seems. Distance < 20 seems almost dead on. > 50 is where problems start. – Parallel Pancakes Dec 25 '20 at 18:30