Translating from Euler rotation to Quaternion rotation - c#

I am using the Leap Motion device to get usable data on the position and orientation of my hand. At the moment I am having trouble with the orientation segment of the coding. The Leap API only has code for frame by frame rotation, however, it also provides a normal vector (normal to the palm of the hand) and a pointer vector (pointing in the direction from the palm outwards towards the fingers). These two vectors are perpendicular.
The vectors:
Leap.Vector normal = current.PalmNormal;
Leap.Vector pointer = current.Direction;
More information can be found on the Leap Hand API: https://developer.leapmotion.com/documentation/Languages/CSharpandUnity/API/class_leap_1_1_hand.html
Converting to Unity Vector3 class:
Vector3 normalU = new Vector3();
normalU.x = normal.x;
normalU.y = normal.y;
normalU.z = normal.z;
Vector3 pointerU = new Vector3();
pointerU.x = pointer.x;
pointerU.y = pointer.y;
pointerU.z = pointer.z;
I use these to vectors to calculate the Euler Angles orientation of my hand (a rotation of theta_x degrees about the x-axis, theta_y degrees about the y-axis, and theta_z degrees about the z-axis) using the code:
float rXn = Mathf.Atan (normalU.y/normalU.z);
rXn *= (180f/Mathf.PI);
if ((normalU.y < 0 && normalU.z < 0) || (normalU.y > 0 && normalU.z < 0))
{
rXn += 180f;
}
float rZn = Mathf.Atan (normalU.y/normalU.x);
rZn *= (180f/Mathf.PI);
if ((normalU.y < 0 && normal.x > 0) || (normalU.y > 0 && normalU.x > 0))
{
rZn += 180f;
}
float rYn = Mathf.Atan (pointerU.x/pointerU.z);
rYn *= (180f/Mathf.PI);
if ((pointerU.x > 0 && pointerU.z > 0) || (pointerU.x < 0 && pointerU.z > 0))
{
rYn += 180f;
}
The Euler Angles are then converted to a Quaternion and implemented using the code:
Quaternion rotation = Quaternion.Euler (-rZn+90, -rYn, rXn+90);
rigidbody.MoveRotation (rotation);
More information on the Unity Quaternion class can be found here: http://docs.unity3d.com/Documentation/ScriptReference/Quaternion.html
As I coded this, I tested each axis of rotation individually, commenting out the others (setting them to 0), and they worked properly. However, when I implemented all three at once, the behaviors of rotations around an individual axis changed, which confuses me. Why would including recognition of rotation about the y-axis change the way rotation about the x-axis occurs?
As each individual axis of rotation worked when the others were commented out (and set to 0), I think the problem lies in the way the Euler Angles are converted to a Quaternion. I do not have a great understanding of the way Quaternions are used to represent rotations, however I am confused as to why changing the value of the angle of rotation about the y-axis would change the angle of rotation about the x-axis.
Thanks for your help.

The order of rotation is relevant, and this might be what causes your confusion. Imagine a point on the x-axis at (1, 0, 0). When we now do a rotation of 90° around the x axis, nothing happens. Then we do a rotation of 90° around the y axis, which makes the point lie on the positive z-axis. If we change the order of rotation, the point will end on the y axis. Depending on the way your functions are implemented, they require a certain order of rotation to get the expected results.

It's not perfect, but I am getting pretty good results with:
private void UpdateHandNormal( Hand hand, Transform marker )
{
float y = Mathf.Atan( hand.Direction.x / hand.Direction.z );
if( float.IsNaN( y ) ) y = 0;
marker.localRotation = new Quaternion( hand.PalmNormal.z, -y, hand.PalmNormal.x, 1 ) ;
}
Where hand is the Hand instance from the leap controller and marker is a simple rectangle representing the hand rotation.
I was getting NaN for y so I added the set to 0 check.
ath
J.

Related

How do I get one of the coordinates of rotation in unity?

I wanted to a cube do not rotate more than 60 degrees so it dosn't rotate to more
I tried to use this
if(gameObject.GetComponent<Transform>().rotation.x >= 60 || gameObject.GetComponent<Transform>().rotation.x <= -60 || gameObject.GetComponent<Transform>().rotation.z >= 60 || gameObject.GetComponent<Transform>().rotation.z <= -60)
{
gameObject.GetComponent<Transform>().rotation = new Quaternion(0, 0, 0, 0);
}
to check the rotation.
It didn't work so I print what rotation is getting in the x coordinate
and it said cordinates like:
5.98324, 7.39482, -1.983495
and I was just moving the x position not the rotation and It change it.
So how do I get the x rotation that it sais in the component transform?
I think the way you approached is fine but not suitable for the scenario. Euler Angles should be handled in order to control the Transformation.
simple understanding be like:
// declare your cube rotation
float moveSpeed = 5f // speed in which cube rotates
float rotatecube = moveSpeed* Time.deltaTime* 10;
//Your condition be like
if(transform.eulerAngles.z < (.....declare the requirement.....))
transform.Rotate(vector3.forward * rotateCube);
If you still face any issue related to angles console.log() is the better way for self understandings.
Hope it clarifies..
You are interested in the euler angles of the rotation, not the quaternion components of the rotation. You should start by referring to transform.eulerAngles.x instead of transform.rotation.x or transform.eulerAngles.z instead of transform.rotation.z.
By the way, it's best to call GetComponent as few times as you can get away with as it's an expensive operation. You should rather call it once and assign the result to a variable such as this:
Transform cubeTransform = gameObject.GetComponent<Transform>();
if(cubeTransform.eulerAngles.x ...)
{
...
}
Additionally, you don't even need to use GetComponent to access the transform of the gameObject the script is attached to. Instead, you can just use transform, e.g.:
if ( transform.eulerAngles.x >= 60 || transform.eulerAngles.x <= -60
|| transform.eulerAngles.z >= 60 || transform.eulerAngles.z <= -60)
{
transform.rotation = Quaternion.identity;
}
Basicaly, you want to make limit of rotation of any axis - x,y, or z.
if yes, then here is the solution :
Program:
Rotating A cube not more that 60 degree in x and z only.
var rotValue += Time.deltaTime;
Cube.transform.rotaion = Quaternion.Eular(Mathf.Clamp(rotaValue,0,60), 0, Mathf.Clamp(rotaValue,0,60));
you can use Mathf.clamp for Clamping any Float value Between min and max.
hope, problem solved.
Skip all the above comments. I found a way to get the exact rotation values as in INSPECTOR. I am showing for angle (x). The code is as follows:
public Transform GameObject;
void Update()
{
float Rotation;
if(GameObject.eulerAngles.x <= 180f)
{
Rotation = GameObject.eulerAngles.x;
}
else
{
Rotation = GameObject.eulerAngles.x - 360f;
}
}

Get angle from Horizontal and Vertical Axis

Trying to implement method to rotate object in unity based on Vertical and Horizontal Axis which gets determined by position of cursor on an image.
Creating a 3D game for mobile with joystick for controls. The aim is to rotate using the joystick.
Example of image:
https://imgur.com/a/hd9QiVe
The green circle moves with users press and returns
X and Y values between -1 to 1 with 0 being in middle.
Just to visualise how input happens:
https://imgur.com/a/8QVRrIh
As shown in image I simply want angle or a way to move object in direction that user input is detected.
Tried several methods for calculating angle by using atan and tan but my maths is quite bad and Im not entirely sure I grab correct values in first place.
//background joystick refers to white circle in first image
pos.x = (pos.x / backgroundJoystick.rectTransform.sizeDelta.x);
pos.y = (pos.y / backgroundJoystick.rectTransform.sizeDelta.y);
inputVector = new Vector3(pos.x * 2f, 0, pos.y * 2f);
inputVector = (inputVector.magnitude > 1.0f) ? inputVector.normalized : inputVector;
//grabbing axis input
public float Horizontal()
{
if (inputVector.x != 0)
{
return inputVector.x;
}
else
return Input.GetAxis("Horizontal");
}
public float Vertical()
{
if (inputVector.z != 0)
{
return inputVector.z;
}
else
return Input.GetAxis("Vertical");
}
As shown in code angle is necessary from input.getaxis for vertical and horizontal to direct object towards the angle.
Currently the formulas used do not provide any angles.
If you want to get the angle of a Vector, use the Vector2.SignedAngle():
var inputAngle = Vector2.SignedAngle(Vector2.right, inputVector);
Angles are relative, which is why you need to specify Vector2.right as the first parameter. There's also a Vector2.Angle() method, but that just returns the distance between two angles, and doesn't take into account the direction.
If you need to verify that your input vectors are what you think they are, use Debug.Log() to print your inputVector.

c# unity error can't flip my submarine on its y axis

This has been an issue for a week now. I can't seem to figure it out. I am trying to flip my submarine on its y axis once its goes upside-down. it rotates on my mouse position.
This code rotates my submarine on mouse position which is perfect.
Vector3 RayPos = cam.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - RayPos;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation,
rotateSpeed * Time.deltaTime);[sub flipping y][1]
Now this code is my problem. Once my sub goes upside down it flips (y) but
it only does it once and then when I go back the correct way it won't flip
on (y) anymore.
Debug.Log(Mathf.Abs(Vector3.Dot(transform.up, Vector3.up)));
if (Mathf.Abs(Vector3.Dot(transform.up, Vector3.down)) < 0.125f)
{
if (Mathf.Abs(Vector3.Dot(transform.right, Vector3.down)) > 0.825f)
{
SubTrans.localScale = new Vector3(transform.localScale.x, -1,
transform.localScale.z);
}
else
{
SubTrans.localScale = new Vector3(transform.localScale.x, 1,
transform.localScale.z);
}
}
if (Mathf.Abs(Vector3.Dot(transform.up, Vector3.down)) < 0.125f)
I assume this makes your code perform the swap only when the sub is ~90 deg to the ground. This is fine because the absolute value means it occurs on both left & right sides.
if (Mathf.Abs(Vector3.Dot(transform.right, Vector3.down)) > 0.825f)
However this is wrong because of the absolute value. It treats up & down the same (comparatively since you're using transform.right), meaning it always flips you because abs(transform.right) is always >0.8 when the previous condition is valid.
You should be checking whether the sub is pointing up or down by checking the up direction like this
if (Mathf.Abs(Vector3.Dot(transform.up, Vector3.up)) > 0f) //less than 90deg angle with up
//flip sub upwards
else
//flip downwards
You can keep the outer if conditional if you want it to flip at certain times, but this is all that's needed.

Work out whether to turn clockwise or anticlockwise from two angles

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;

Reversing the direction of an object after a collision

Edit2: See below for new problem.
I'm creating a Windows Phone 7 game and have run into some difficulty. What I'm trying to do is use touchscreen coordinates converted to radians to fire an object upwards at an angle. That part works fine.
However, I cannot figure out how to make the object bounce off the side walls. I had it working in an earlier implementation, and it was simply a matter of negating the Y value when colliding with a wall. Unfortunately, it's not so easy this time. When the object hits the wall, it bounces off the wall for a short distance before changing direction towards the wall again.
How can I structure my code so it stops getting pushed into the wall?
Thank you.
//Move bubble
public void MoveBubble(Bubble b, double radians)
{
if (b.stop == false)
{
{
//Move bubble 'up'
Y = (float)(speed * Math.Cos(radians));
X = (float)(speed * Math.Sin(radians));
}
b.bubblePosX += X;
b.bubblePosY += Y;
//Problem area
if(b.bubblePosY <= 0)
Y-=Y;
if(b.bubblePosY >= 350)
Y-=Y;
}
//Calculate Angle
private void CalculateAngle(int touchX, int touchY)
{
radians = (Math.Atan2(touchX - bubbleStart.X, touchY - bubbleStart.Y));
}
Edit2: Just wanted to add the latest code. Now the ball is bouncing off the walls perfectly, but I'm having an issue with the direction bool being initialized to true. Sometimes, when firing the object, the angle is reversed. How can set the initial bool to the appropriate true/false value?
//Move bubble
public void MoveBubble(Bubble bubble, Vector2 distance)
{
if (bubble.stop == false)
{
float velocityX = speed * distance.X;
float velocityY = speed * distance.Y;
bubble.X += velocityX;
if (direction == true)
bubble.Y += velocityY;
if (direction == false)
bubble.Y -= velocityY;
if (bubble.Y <= 0)
direction = !direction;
if (bubble.Y >= 350)
direction = !direction;
}
}
You're not negating the Y value, you're just subtracting its value from itself. Perhaps you meant to write:
Y = -Y;
Or am I missing something?
You aren't reversing the direction of the bubble movement. the most suitable solution for changing at that moment the direction would be:
public void MoveBubble(Bubble b, double radians)
{
if (b.stop == false)
{
{
//Move bubble 'up'
Y = (float)(speed * Math.Cos(radians));
X = (float)(speed * Math.Sin(radians));
}
b.bubblePosX += X;
//Problem area
if(b.bubblePosY <= 0 || b.bubblePosY >= 350)
b.bubblePosY -= Y;
else
b.bubblePosY += Y;
}
}
That portion of code would move backwards the bubble just for the next frame, to get the bubble change absolutely the direction of its movement, you would have to recalculate the radians getting the point where the bubble finds a collision and the old touch points.
In resume, in each point of collision, you would have to recalculate the direction is getting your bubble, if you goal this, you can have the whole problem solved, reflect direction in case of collision.
I was looking for some code I had, but I found I've lost it, but I hope mi explanation could help.
See you
You have to change the location and the velocity.
If you have a position of (0, -1) and a velocity of (1, -1) and you can't have negative values, you have to switch the y-value (as to make up for the velocity) to (0, 1) and change the velocity to it's negative (1, 1).
so, loc(ation) and vel(ocity) has to change for instance like:
loc(0, 1), vel(0, -1) // OK
loc(0, 0), vel(0, -1) // switch vel to y=-y; (so -1 becomes 1)
loc(0, -2), vel(1, -3) // switch loc to it's positive value, loc(0, 2)
// and the vel to it's opposite (1, 3)
loc(-1, -3), vel(-2, -4) // loc is set to (1, 3) an vel to (2, 4)
// since both X and Y have been breached
Do this (assuming X and Y are velocity):
if(b.bubblePosY <= 0 && Y < 0)
Y = -Y;
if(b.bubblePosY >= 350 && Y > 0)
Y = -Y;
What is happening in your code is that your bubbles are getting stuck in your wall. The code above only reflects the bubble's direction if it is moving into the wall. Once it is moving out of the wall, it ignores that wall.
Additionally, rather than calculating the angle from the difference in positions and then converting it back to a direction vector, try simply normalizing difference in positions to get a direction vector (a unit vector; length = 1):
Vector2.Normalize(new Vector2(touchX - bubbleStart.X, touchY - bubbleStart.Y))
You should be storing either velocity vector or the direction vector between frames, so that when you reflect your bubble it stays going in that direction until it is reflected again.

Categories

Resources