I currently have this code in my game:
Vector2 pixelpos = new Vector2(x, y);
Vector2 center = new Vector2(t.Width / 2, t.Height / 2);
Vector2 pixelposWorld = (pixelpos - center);
float rotation = (float)Math.Atan2(pixelposWorld.Y, pixelposWorld.X);
float rotationPercent = (MathHelper.ToDegrees(rotation) / 360);
My goal is to end up with rotationPercent to be a value between 0.0 and 1.0, 0 degrees being 0.0, 180 being 0.5 and 360 being 1.0.
Currently, rotationPercent only comes out as 1.0.
What can I do to fix this?
First of all, in your case, there is no need to convert it to degrees, since Math.aTan2 returns the angle in radians, just divide your rotation variable by (2*Pi).
Secondly, check what you are doing at "t.Width / 2, t.Height / 2", as you haven't specified in your question what 't' is, make sure it's members are not integers.
Now as far as your problem itself, there is not enough information supplied. Where does this contain your rotation information? Is the 'pixelpos' vector your world space position, or do you also use that as rotation?
Brought back to a minimum, the following code works roughly like like you described?
Vector2 pixelpos = new Vector2(0, 1);
float rotation = (float)(Math.Atan2(pixelpos.Y, pixelpos.X) / (2 * Math.PI));
Which results 0.25, or 90 degrees.
The best way to calculate this would be through the use of dot products though:
Vector2 pixelposWorld = Vector2.Normalize(pixelpos - center);
float dot = Vector2.Dot(Vector2.UnitX, pixelposWorld);
float rotation = (pixelposWorld.Y >= 0)
? (1f - dot) / 4f
: (dot + 3f) / 4f;
Just saying, it is on average 120% faster (I ran the tests).
I think you forgot to use float in 360, it should be 360f.
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 wrote a algorithm. Its normally working.
Just i can not get right angle of x when y or z changes
float x = Mathf.Atan2(transform.forward.y, transform.forward.z) * Mathf.Rad2Deg
This code is giving the right angle when my object angles are (x,0,0).
But when the y or z change(x,35,46), this code is giving false angle.
By the way i want to get 0-360 angle.
If i get this angle, code will work(i tested it).
So i am trying to get the rotation of x axis 0-360.
But the atan2 is not giving the right value.
Maybe i can use Vector3.Angle but it doesn't work that i want.
I don't ask too many questions in stackoverflow so if you didn't understand please tell me which part didn't you get it?
If I understand you correct you want the objects rotation around the X axis (global or local).
You could probably simply use Transform.eulerAngles something like
var x = transform.eulerAngles.x;
if(x < 0) angle += 360;
Or if you want the local rotation (relative to the parent) Transform.localEulerAngles
var x = transform.localEulerAngles.x;
if(x < 0) angle += 360;
No, I wouldn't figured out
int sign = (transform.forward.y<0) ? 1 : -1;
float x = (Vector3.Angle(transform.position, transform.forward) - 38) * sign * 180 / 100;
This code is just working on 0,90,0 angle
I still can not reach the right angle when the rotation change
I found some code with the combination of Cross,Dot,Angle:
float Angle360(Vector3 v1, Vector3 v2, Vector3 n)
{
float angle = Vector3.Angle(v1,v2);
float sign = Mathf.Sign(Vector3.Dot(n, Vector3.Cross(v1, v2)));
float signed_angle = angle * sign;
return (signed_angle + 180) % 360;
}
This code is not working too
It will be made probably with Vector3.Angle or Dot vs.
How can i find right angle with Vector3.Angle,
The value 38 is changing
Vector3.Angle(transform.position, transform.forward)
This code is showing the angle but when the rotation change it gives false value.
how can i get the angle of x when objects look change.
So this code is giving right when the value is x,0,0.
Mathf.Atan2(transform.forward.y, transform.forward.z) * Mathf.Rad2Deg
I think i am not using Vector3.Angle Correctly
I need to get the x value when the y and z values are different
user starts from A and moves to C though(via) B (sample points on screen) in unity3d. at this point, i have calculated angle (theta) which in both images is almost 45 deg(almost). problem is i wanted to conclude that in left image user intended CounterClockWise motion and in right image user intends clockwise rotation.
ahh, its really complicated than i imagined, please suggest.
currently unity code is like
protected void calcAngles(){
v1 = go2.position - go1.position;
v2 = go3.position - go1.position;
v3 = go3.position - go2.position;
float angle = Vector3.Angle (v2, v1);
Debug.Log ("angle:" + angle.ToString ());
//float atan = Mathf.Atan2 (Vector3.Dot (v3, Vector3.Cross (v1, v2)), Vector3.Dot (v1, v2)) * Mathf.Rad2Deg;
//Debug.Log ("atan2:" + atan.ToString ());
}
any ideas.? psudo code.? huge thanks in advance. cheers,lala
It is incredibly difficult to do this.
This may help...
private float bestKnownXYCWAngleFromTo(Vector3 a, Vector3 b)
// the best technology we have so far
{
a.z = 0f;
b.z = 0f;
float __angleCCW = Mathf.Atan2(b.y, b.x) - Mathf.Atan2(a.y, a.x);
if ( __angleCCW < 0f ) __angleCCW += (2.0f * Mathf.PI);
float __angleCWviaatan = (2.0f * Mathf.PI) - __angleCCW;
__angleCWviaatan = __angleCWviaatan * (Mathf.Rad2Deg);
if ( __angleCWviaatan >= 360.0 ) __angleCWviaatan = __angleCWviaatan-360.0f;
return __angleCWviaatan;
}
note that this is a 2D concept, modify as you need.
note that obviously "a" is just your (b-a) and "b" is just your (c-a)
Please note that true gesture recognition is a very advanced research field. I encourage you to get one of the solutions out there,
https://www.assetstore.unity3d.com/en/#!/content/14458
https://www.assetstore.unity3d.com/en/#!/content/21660
which represent literally dozens of engineer-years of effort. PDollar is great (that implementation is even free on the asset store!)
Uuuups. My answer from before was completely wrong. It seems that Vector3.Angle always gives a unsigned angle. But we need the sign to understand whether is rotating clockwise or counterclockwise.
Now, this piece of code will give you a SIGNED angle between your two vectors. The Normal argument should be the normal to the plane you want to consider.
float SignedAngle(Vector3 from, Vector3 to, Vector3 normal){
float angle = Vector3.Angle(from,to);
float sign = Mathf.Sign(Vector3.Dot(normal, Vector3.Cross(from,to)));
float signed_angle = angle * sign;
return signed_angle;
}
For a screen overlay I am making for a 3-dimensional game, I need to display icons over certain locations in the 3D world. I've managed to get several variables from the game that should make this possible:
Player position (x,y,z)
Camera position (x,y,z)
Point position (x,y,z)
Camera Angle(FOV)
Using these variables I managed to get the right and left edge of the camera view. Using these 2 variables I am able to get the point between 0 and 1 where the icon should be displayed on the x axis. (0 is on the left edge, 1 is right edge) This seems to work quite well, but only if I am aligned to either the X or Z axis, as shown in the following example:
I've been trying to fix this using the following rotation matrix:
[ Math.Cos(angle), -Math.Sin(angle) ]
[ Math.Sin(angle), Math.Cos(angle) ]
What I do is, I put the player position, camera position and the camera edge positions in the matrix with as rotation point the world point. The problem is, as soon as I put the angle amount at 90 degrees, the X and Y are being flipped. I've been trying to find a solution for this for a few days now, but I can't find it, so I hope anyone can push me in the right direction here. Below are a few parts of my code that might help in finding the solution:
float PCDistXZ = (float)Math.Sqrt(deltaPCx * deltaPCx + deltaPCz * deltaPCz); // X/Z distance from the world point to the camera
Point fakeAvatarPos = RotateAround(new Point((int)point.x, (int)point.z), new Point((int)avatar.x, (int)avatar.z), (int)90);
Point fakeCameraPos = RotateAround(new Point((int)point.x, (int)point.z), new Point((int)camera.x, (int)camera.z), (int)90);
double edgeRight = fakeC.X + (Math.Sin(45) * PCDistXZ);
double edgeLeft = fakeC.X - (Math.Sin(45) * PCDistXZ);
float edgeTest_ScreenPositionX = (1 - (float)((edgeRight - P.x) / (edgeRight - edgeLeft))) * screenWidth;
public static Point RotateAround(Point pCenter,Point pPoint, float pAngle)
{
double angle = (pAngle * Math.PI) / 180;
double[,] matrix = new Double[2, 2] {
{ Math.Cos(angle), Math.Sin(angle) },
{ Math.Sin(angle), Math.Cos(angle) }
};
double xOffset = pPoint.X - pCenter.X;
double yOffset = pPoint.Y - pCenter.Y;
Point newPoint = new Point(
(int)(((pPoint.X - xOffset) * matrix[0, 0]) - ((pPoint.Y - xOffset) * matrix[0, 1])),
(int)(((pPoint.X - yOffset) * matrix[1, 0]) + ((pPoint.Y - yOffset) * matrix[1, 1]))
);
newPoint.X += (int)xOffset;
newPoint.Y += (int)yOffset;
return new Point(newPoint.X,newPoint.Y);
}
Note: I've changed the names of some of the variables to more understandable one, so it could be possible that there are inconsistencies in the names.
EDIT: I found out about view- and projection matrices. I might be able to use those to convert the 3D position to screen. I'm not sure if it's possible to make this matrices with the limited information I have though.
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);
}
}