Here it goes, I am making a climbing system in a Unity project where depending on the direction your speed is changed. In my case it would be a speed of 1 for going up, 1.5 for sideways and 2 for going down.
This is how I calculate angles right now:
float angle = (Mathf.Atan2(this.characterController.GetAxisControlValue(CharacterAxisControl.Vertical), this.characterController.GetAxisControlValue(CharacterAxisControl.Horizontal)) * Mathf.Rad2Deg);
angle %= 360.0f;
if (angle < 0.0f)
{
angle += 360.0f;
}
GetAxisControl value returns a vale between -1 and 1. Now I need to find out how you would get the average speed between point like this: Example
I'm searching for a formula that can solve this problem.
Could anyone assist me please, pretty please.
If you want it to be proportional to the angle, then it's easy:
var speed = (angle % 180) / 180 + 1;
This will give you:
0 deg 1
90 deg 1.5
180 deg 2
270 deg 1.5
45 deg 1.25
150 deg 1.83 // this is your picture example
If you want any arbitrary speeds you could use linear interpolation. Let's say you want speed Vu for going up, Vs for going sideways and Vd down.
var t = (angle % 180) / 90; // we only care about vertical direction
var speed = t < 1
? Vu * (1 - t) + Vs * t // this is for picking the value in the range 0..90
: Vs * (2 - t) + Vd * (t - 1); // this is in range 90..180
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.
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
Feel free to mark my question duplicated. Because I know absolute nothing about COS, SIN, and TAN and someone else probably already ask this question.
So, I was try to set the circular progress bar based on x and y axis that can get from gamepad input. The progress bar put it simple is just a Minimum of 0 and maximum of 360.
I did try to search a bit, but my best understanding is that it work with only 180 degree and positive x and y. But the input I get from the controller is and y from -1 to 1 (where x -1 is left and 1 is right, y -1 is bottom and 1 is top)
Here is my code so far.
var controller = Windows.Gaming.Input.Gamepad.Gamepads[0].GetCurrentReading();
x = controller.LeftThumbstickX
y = controller.LeftThumbstickY
//what do I have to do from here?
progress.Value = angle; //?
The trigonometric function atan2 is the tool for this job. In C#, this is implemented by Math.Atan2 :
double angleInRadians = Math.Atan2(y, x);
double angleInDegrees = (180 / Math.PI) * angleInRadians;
Using this formula with (for instance) parameters (1,1), you'll get a result of 45.
However, in terms of polar alignment, this angle measures anti-clockwise from "east". To convert this to an angle that measures clockwise from "north":
double compassRadians = Math.PI / 2 - angleInRadians;
double compassDegrees = (180 / Math.PI) * compassRadians;
but now we may encounter negative values, so we can normalize them with the following method:
double normalizeDegrees(double a) => ((a % 360) + 360) % 360; //convert to 0-360
then
var compassAngle = normalizeDegrees(compassDegrees);
The method you want is Math.Atan2. This takes two arguments - the y-value first, then the x-value - and it gives you an angle in radians.
Since you want an angle in degrees, you'll need to convert - the conversion factor is 180 / Math.PI. So you'll be using something like:
var radiansToDegrees = 180 / Math.PI;
progress.Value = Math.Atan2(y,x) * radiansToDegrees;
Depending exactly what combination of x and y needs to correspond to 0 you might need to add a number of degrees on afterwards. This as-is will give you 0 degrees for x = 1, y = 0, and 90 degrees for x = 0, y = 1, etc.
I'm trying to calculate distance of cannon shot using velocity and angle. I'm testing results using utility tests. The formula for range should be someting like v^2 * sin2a aka velocity squared * sin2alpha . As far as i know, sin2a is supposed to be 2*sina*cosa, but i may be wrong.
Anyway, whatever i do, i get wrong results, because it doesn't seem to be calculating sin.
Here's the code
Cannon.cs
public int CalculateDistance(int angle, int velocity)
{
int distance = 0;
double radian_angle = (Math.PI / 180) * angle;
distance_of_shot = (Math.Pow(velocity, 2)) * (2 * Math.Sin(radian_angle) * Math.Cos(radian_angle));
distance = (int)distance_of_shot;
return distance;
}
CannonAttackTest.cs
[TestMethod]
public void Calculations()
{
Canon new_canon = new Canon();
var data = new_canon.CalculateDistance(45, 450);
Assert.AreEqual(20682, data);
}
The results is suppose to be 20682, but i get 202500, which is exactly a number of squared 450...whichs points to sin not being calculated.
Any help is appreciated!
Thank you!
Check your units, you need to divide by the value of "g" because velocity is m/s and your "distance of shot" is in m^2/s^2.
distance_of_shot = (Math.Pow(velocity, 2)) * (2 * Math.Sin(radian_angle) * Math.Cos(radian_angle))/9.81;
You have a mistake sin 0.70710678118654746 and cos 0.70710678118654757 but after
(2 * Math.Sin(radian_angle) * Math.Cos(radian_angle)) result coming 1
I am trying to ease (gradually move) the rotation of an object to an arbitrary position. The angle of rotation is determined by a Virtual Thumbstick class which returns X/Y coordinates between -1 and 1. If there is no movement on the thumbstick, I am rotating back to point to 0, except I am compensating for the angle of the sprite's image.
The problem I am having is that this code will only allow approximately 1.5 rotations (anywhere between -3*PI and 3*PI) instead of continuous rotation. Using Math.Atan2 with the X/Y coords of the thumbsticks, the returned angle is constrained between -PI and PI but allows continuous rotation. Also, if I rotate the object in one direction and release the thumbstick, it will rotate back to top from the direction it came. I want it to rotate back to the top on the shortest route.
if (VirtualThumbsticks.LeftThumbstick.Length() > .2f)
{
double rotateTo = Math.Atan2(VirtualThumbsticks.LeftThumbstick.Y, VirtualThumbsticks.LeftThumbstick.X);
if (rotateTo > Rotation + Math.PI) rotateTo -= (Math.PI * 2);
if (rotateTo < Rotation - Math.PI) rotateTo += (Math.PI * 2);
Rotation += (rotateTo - Rotation) * 0.2;
}
else
{
Rotation += (-1.57079 - Rotation) *0.2;
}
If there are any Flash/ActionScript game developers that know what I'm talking about, please chime in as I can apply that to C#.
Thanks in advance, everyone!
EDIT:
This chunk of code works flawlessly in AS3:
function enterFrameHandler(e:Event):void
{
var curMouseX = Math.round(-(arrow.x - stage.mouseX));//(stage.stageWidth/2)-(stage.mouseX/2);
var curMouseY = Math.round(-(arrow.y - stage.mouseY));//(stage.stageHeight/2)-(stage.mouseY/2);
var angleTo:Number = Math.atan2(curMouseX, -curMouseY) * TO_DEGREES;
if (angleTo > arrow.rotation+180) angleTo -= 360;
if (angleTo < arrow.rotation-180) angleTo += 360;
tf_angle.text = angleTo.toString();
tf_mouseX.text = curMouseX.toString();
tf_mouseY.text = curMouseY.toString();
arrow.rotation += (angleTo - arrow.rotation) * 0.2;
}
I'm beginning to wonder if there is an issue with my types or typecasting that is causing the problem. If anyone has any ideas, your input is greatly appreciated.
This is written in C# using XML, so the actionscript parallel may not apply, but i bet that it does. In Flash, rotations are adjusted at the end of every frame, because there is little benefit in storing a rotation transform of 123456789 degrees. so the transform resets to the modulo value of the rotation.
However, in calculating shortest rotation, you can run into problems if you are expecting some large number and comparing it to some other number instead of calculating their relative positions using their modulo values.
Maybe this is the source of the problem. its just a guess.
On the odd chance you are not familiar with it, the modulo operator - aka percent sign - "%" will give you the remainder of division. this is helpful for numbers that loop, like grid alignments, and rotations.
Example:
21 % 5 = 1;
720 % 360 = 0;
The issue has been solved. The problem was lying in the rotation value not being "normalized". Basically, if the rotation value exceeds Math.PI*2, it must return to 0... and if the rotation value falls below 0, it must return to Math.PI*2.
The last comment on this page has solved my problem and the rotation is normalized now.
http://forums.create.msdn.com/forums/t/27527.aspx
My resulting code:
if (VirtualThumbsticks.LeftThumbstick.Length() > .2f)
{
float angleTo = (float)Math.Atan2(VirtualThumbsticks.LeftThumbstick.Y, VirtualThumbsticks.LeftThumbstick.X);
if (angleTo > Rotation + Math.PI) angleTo -= (float)(Math.PI * 2);
if (angleTo < Rotation - Math.PI) angleTo += (float)(Math.PI * 2);
accelerationRotation += (angleTo - Rotation) * (float)0.25;
}
else
{
accelerationRotation += ((float)(Math.PI / 2) - Rotation) * (float)0.2;
}
Rotation = Wrap(accelerationRotation, 0, (float)Math.PI * 2);
The Wrap function is as follows:
public static float Wrap(float value, float lower, float upper)
{
unchecked
{
if (lower >= upper)
{
throw new ArithmeticException("rotary bounds out of negative or zero size");
}
float distance = upper - lower;
float times = (float)Math.Floor((value - lower) / distance);
return value - (times * distance);
}
}