public double GetPitchToFace(double Z2, double Z1, double X2, double X1)
{
double Arc;
Arc = Math.Atan2(Z2 - Z1, X2 - X1);
Arc = (Arc >= 0) ? Arc+Math.PI : (2 * Math.PI + Arc);
return Arc;
}
I am attempting to calculate the correct pitch of a player in-game in order to face and travel to a waypoint.
It might be worth noting that the games coordinate system is Right-Handed.
I've confirmed that ATan2 is receiving the correct values yet the return value is incorrect.
It seems to be returning a downward pitch despite the waypoint being above the player. Yet other times it returns the seemingly correct pitch - I suspect this might have something to do when the point / player shift quadrants ?
(Minor side note - the reason I am 2 * Math.PI if Arc is >= 0 is simply the way the game stores and manages player pitch more info can be found here :
Pitch-Ingame
)
Here is an example of the above manually calculated :
Atan2(Z2 - Z1, X2 - X1)
Arc = Atan2(179.7 - 157.9, -3457.1-(-3432.1))
Arc = Atan2(21.7,-25.3)
Arc = 2.4 Radians
Arc = Arc + PI (without adding PI we return 137.51 degrees this is the opposite angle I need so I add PI to flip it to the correct side or add 180 degrees)
Arc = 5.5 Radians
Arc = 315,127 Degrees (this is a downwards trend from the point meaning we still miss the end point)
Here is the really bad plot of the above points
Please keep in mind that the points are plotted such as X,Z not X,Y
Related
This is the code that rotates the boat to follow the line. A joint is just a Vector2 on the line. To the left is 0 degrees. And as you can see, when the boat is going from 0 to 360 or vice versa degrees, it glitches out.
float LookAt(Vector2 joint)
{
float deltaY = rect.y - joint.Y; // Calculate Delta y
float deltaX = joint.X - rect.x; // Calculate delta x
float angle = (float)(Math.Atan2(deltaY, deltaX) * 180.0 / Math.PI) + 90; // Find angle
float amountToRotate = angle - rotation;
amountToRotate *= 0.05f;
Console.WriteLine($"Rotation: {rotation} Angle: {angle} Amount: {amountToRotate}");
return rotation + amountToRotate;
}
I'm using an amountToRotate variable because I want the rotation to be a little smooth (doesn't show well on the GIF).
https://gyazo.com/cd907763665ac41a2c8f8e5d246ab292
Any help is much appreciated.
(I'm also doing this in Raylib if that makes any difference).
Because atan2() returns values between -PI and +PI or between -180 and +180 degrees.
So, if your boat is looking at something like 170 deg and the next joint is at 179 deg, then your amountToRotate is +9 deg, which is fine.
But, if your boat is looking at 180 degs and your the joint is at -180 deg, your amountToRotate is suddenly -360 deg (-180 - 180), which is facing right towards the positive x-axis. The you take 5% off of amountToRotate and add it to your current rotation (180 - 360*0.05 = 162) which means, that the boat is turning away from the node.
As a quick solution, you could convert the angle to full 360 degrees:
angle = (angle + 360) % 360;
But you will still get problems in the direction of the positive x-axis. The better solution would be to calculate the angle between two vectors and invert it:
angleTowardsV2FromV1 = -(Math.atan2(v1.y, v1.x) - Math.atan2(v2.y, v2.x))
in your case this would look something like:
angle = (-(Math.atan2(Math.sin(rotation*Math.PI/180), Math.cos(rotation*Math.PI/180)) - Math.atan2(deltaY, deltaX)))*180/Math.PI
And also, if you only take 5% of your angle, the rotation will never get there. I think, it would much wiser to clamp the angle to +-5deg:
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
angle = clamp(angle, -5, +5);
And the just return:
return rotation + angle;
I hope this helps.
I am trying to code for a game I am working on a specific curve with a specific rotation. I am not a great mathematician... At all... Tried searching for solutions for a few hours, but I'm affraid I do not find any solution.
So, a small picture to illustrate first:
This is an eighth of a circle, radius of 9, beggining is (0,0)
The end is now at about 6.364, -2.636. But I need this same curve, with a 45° direction at the end, but ending at aexactly 6.0,-3.0.
Could any of you show me how to do this? I need to be able to calculate precisly any point on this curve & its exact length. I would suppose using some kind of eliptical math could be a solution? I admit my math class are reaaaly far now and have now good clue for now...
Thank for any possible help
I think I found a quadratic curve which sastisfies your requirement:
f(x) = -1/12 x^2 + 9
Copy the following into https://www.desmos.com/calculator to see it:
-\frac{1}{12}x^2+9
f'(x) would be -1/6x, so when x=6, the derivative would be -1, which corresponds to a -45° inclination. There are probably infinite curves that satisfy your requirement but if my calculus isn't too rusty this is one of them.
I tried to fit an ellipse with foci starting at y=6 here and starting at y=9 here to your points but the slope doesn't look like 45°.
Also starting at any height k, here doesn't seem to work.
I don't think you've fully understood the question I asked in the comments about the "inclination" angle. So I will give a general case solution, where you have an explicit tangent vector for the end of the curve. (You can calculate this using the inclination angle; if we clarify what you mean by it then I will be happy to edit with a formula to calculate the tangent vector if necessary)
Let's draw a diagram of how the setup can look:
(Not 100% accurate)
A and B are your fixed points. T is the unit tangent vector. r and C are the radius and center of the arc we need to calculate.
The angle θ is given by the angle between BA and T minus π/2 radians (90 degrees). We can calculate it using the dot product:
The (signed) distance from the center of AB to C is given by:
Note that this is negative for the case on the right, and positive for the left. The radius is given by:
(You can simplify by substituting and using a cosine addition rule, but I prefer to keep things in terms of variables in the diagram). To obtain the point C, we need the perpendicular vector to AB (call it n):
Now that we have the radius and center of the circular arc, we still need to determine which direction we are moving in, i.e. whether we are moving clockwise or anti-clockwise when going from A to B. This is a simple test, using the cross-product:
If this is negative, then T is as in the diagram, and we need to move clockwise, and vice versa. The length of the arc l, and the angular displacement γ when we move by a distance x along the arc:
Nearly there! Just one more step - we need to work out how to rotate the point A by angle γ around point C, to get the point we want (call it D):
(Adapted from this Wikipedia page)
Now for some code, in case the above was confusing (it probably was!):
public Vector2 getPointOnArc(Vector2 A, Vector2 B, Vector2 T, double x)
{
// calculate preliminaries
Vector2 BA = B - A;
double d = BA.Length();
double theta = Math.Acos(Vector2.DotProduct(BA, T) / d) - Math.PI * 0.5;
// calculate radius
double r = d / (2.0 * Math.Cos(theta));
// calculate center
Vector2 n = new Vector2(BA.y, -BA.x);
Vector2 C = 0.5 * (A + B + n * Math.Tan(theta));
// calculate displacement angle from point A
double l = (Math.PI - 2.0 * theta) * r;
double gamma = (2.0 * Math.PI * x) / l;
// sign change as discussed
double cross = T.x * BA.y - T.y * BA.x;
if (cross < 0.0) gamma = -gamma;
// finally return the point we want
Vector2 disp = A - C;
double c_g = Math.Cos(gamma), s_g = Math.Sin(gamma);
return new Vector2(disp.X * c_g + disp.Y * s_g + C.X,
disp.Y * c_g - disp.X * s_g + C.Y);
}
I'm having an issue where I can't figure out the algorithm to find out the destination point based on objects rotation and the amount to move. I have to move to the direction of my rotation a certain amount, but I don't know how to calculate the destination point I end up being at. Example:
Object location = (0, 0)
Object rotation = 45
Amount to move = 4
with these variables the destination point would be (2.5, 2.5)
Example 2:
Object location = (0, 0)
Object rotation = 0
Amount to move = 4
and with these it would be (0, 4)
The problem is, I don't know how to calculate the destination point when I know those variables. I need an algorithm that will calculate the destination point, can somebody help with this? :)
Regards, Tuukka.
If this is a strictly algorithmic question where you want to calculate the destination point (i.e. no game object to move around, but abstract data), you can do this:
Consider the two-dimensional plane in cartesian coordinates, (i.e. the standard x/y system). Let O be an object at point (0,0). From your "destination point" (2.5, 2.5) I can assume that you want the following thing:
So 45° is the angle and 4 (amount to move) is the length of the line segment you want to move along. Starting from (0,0), this end point can be calculated using sine and cosine by using the formula for the polar representation of a point:
But actually, that image is wrong, which we'll see in the following computation. If the movement is along the line with a slope angle of 45°, you'd land a little bit elsewhere.
Anyways, for this example, alpha would be 45° which is pi/4 in radians (you get this by dividing by 180 and multiplying with pi), and the radius r would be 4 (the amount we want to move), so we'd have calculated the destination point as:
If the point is located anywhere in the room (not at (0,0) but at (x_0, y_0)), then you can still add it as an offset:
So in code you'd write:
public static Vector2 ComputeDestination(Vector2 origin, float amountToMove, float angle)
{
//convert degrees to radians
var rad = angle * Mathf.Deg2Rad;
//calculate end point
var end_point = origin + amountToMove * new Vector2(Mathf.Cos(rad), Mathf.Sin(rad));
return end_point;
}
float homMuchToMove = 4f;
float angle = 45f;
float pointX = Mathf.Cos (ConvertToRadians (angle)) * homMuchToMove;
float pointY = Mathf.Sin (ConvertToRadians (angle)) * homMuchToMove;
public float ConvertToRadians(float angle)
{
return (Mathf.PI / 180f) * angle;
}
For these values you will get both points at 2.828427f
In my C# application , i want to restrict the horizontal and vertical scrolling by computing the mouse movement angle difference.
Grater than 60 degree angle need to be tracked as vertical scroll.
I have the initial X,Y positions and current X, Y positions.How can i calculate the angle of mouse movement.
Any help will be appreciated.
This will give you the angle in radians:
int dx = Math.Abs(x2 - x1);
int dy = Math.Abs(y2 - y1);
double angleRadians = Math.Atan2(dy, dx);
double angleDegrees = (angleRadians * 180)/Math.PI;
You will probably want to ignore angles for which dx or dy is less than a certain number (something like 16 would do it).
Im making a game in XNA.
I have enemies and player.
The enemies should turn gradually towards the player. They should work out whether they need to turn clockwise or anticlockwise, whichever is shorter.
I got the angle the enemy is currently facing and the angle it should be facing (the angle of the line between the enemy and the player) as radians by using Atan2.
I get some weird behavior though. Lets say in the scenario below. the enemy might turn all the way around in the wrong direction.
My code (below) keeps getting longer and I'm still having issues. This code is part of the enemy classes Update method.
This must be a common problem to overcome in games. Is there some way of dealing with this?
//this bit is just in case enemy has rotated more than 360 degrees (gets set back to 0)
if (Math.Abs(_blocklist[0]._floor.Revolutions) >= 2)
{
_blocklist[0]._floor.Rotation = 0.0f;
}
//enemy rotation in radians
float blockroat = _blocklist[0]._floor.Rotation;
// vector to player - vector to enemy
_vectToPlayer = playerpos - _blocklist[0].Centre
angletoplayer = (float)(Math.Atan2(_vectToPlayer.Y, _vectToPlayer.X));
diff = blockroat - angletoplayer;
if (diff < -Math.PI)
{
diff += (float) Math.PI;
diff = -diff;
}
else if (diff > Math.PI)
{
diff -= (float)Math.PI;
diff = -diff;
}
// if enemy angle if off by a certain amount
if (Math.Abs(diff) >_maxturn)
{
if (diff < 0)
{
//turn clockwise
_blocklist[0]._floor.Rotation += _maxturn;
}
else
{
//turn anti clockwise
_blocklist[0]._floor.Rotation -= _maxturn;
}
}
UPDATE
I ended up using method 2 like this.. Works perfectly. Also it is a lot neater than my previous code
//enemy rotation in radians from farseer (red line)
float brot = _blocklist[0]._floor.Rotation + ((float)Math.PI/2);
//vector from enemy to player (blue line)
Vector2 _vectToPlayer = playerpos - _blocklist[0].Centre;
//cross product of 2d vectors
cross = (_vectToPlayer.X * (float)Math.Sin(brot)) - ((float)Math.Cos(brot) * _vectToPlayer.Y);
//tolerance for how closely enemy must point towards player
if (Math.Abs(cross) > 5)
{
if (cross > 0)
{
//turn anticlockwise
_blocklist[0]._floor.Rotation -= _npcstats.maxturnspeed;
}
else
{
//turn clockwise
_blocklist[0]._floor.Rotation += _npcstats.maxturnspeed;
}
}
I think that my previous code was more or less doing exactly the suggested method 1.
But I could not get it to work.. I put this down to the vagaries of farseers coordinate system + how it interacted with my own.
Technique #1:
You are using a convention that I'm not familiar with. In your convention, east is 0, north is -π/2, west is both π and -π, and south is π/2. All angles are between -π and π.
Normally the angle of a character facing east is zero, north is π/2, west is π, and due south is 3π/2. All angles are between 0 and 2π.
Let's assume the normal convention rather than your convention. Start by getting your red and blue vector angles correct in the normal convention; how you do that is up to you.
Subtract the angle of the red vector from both angles. Now we have the guy at the origin facing due east.
Now normalize the new blue angle; if it is smaller than 0, add 2π. If it is larger than 2π, subtract 2π. Do that until it is between 0 and 2π.
Now we have two angles; the angle of the new red vector is zero and the angle of the new blue vector is between 0 and 2π.
If the angle of the new blue vector is less than π then the character at the origin needs to turn towards its left. If it is greater than π then turn right.
Technique #2:
Take a non-zero point on your blue and red vectors, say (bx, by) and (rx, ry). Now compute bx * ry - by * rx. If it is positive, turn right, if it is negative, turn left. If it is zero then either they are facing directly towards or directly away; in that case you'll have to figure out which case you're in by some other means. (This is essentially Jacek's answer stated more directly.)
If you have both blue and red vectors as Vector3, you can do:
Vector3 crossProduct = Vector3.Cross(red, blue)
if (crossProduct.z > 0)
// Turn Right
else
// Turn Left
very easy. say you got 2 angles alpha and beta and you would like to tell shortest movement from alpha to beta is clockwise or anti clockwise. what you should do is this:
set alpha to be 0 and add the same offset you gave alpha to beta.
now if beta is above 180- movement is anticlockwise, else, movement is clockwise.
for example: alpha is 10 deg and beta is 350 deg. so -10 deg offset to both angles sets alpha to be 0 and beta to be 340 and movement is anticlockwise.
def direction_by_2_angles(alpha, beta)
# true clockwize, false not clockwize
delta = 360 - alpha
beta = beta + delta
beta = beta % 360
beta < 180 ? true : false
end
I searched to find if there is an super-short answer. Didn't found one. Seem like Technique 1 is popular. However i already implemented it so here you are:
//ccw = 1, cw = -1
//refineAngle = normallize angle to range [-PI, PI]
currentAngle = refineAngle(currentAngle);
targetAngle = refineAngle(targetAngle);
if(targetAngle < 0)
targetAngle += (PI *2);
if(currentAngle < 0)
currentAngle += (PI *2);
if(targetAngle < currentAngle)
targetAngle += (PI *2);
if(targetAngle - currentAngle <= PI)
return 1;
else
return -1;