- Home /
Rotation Jumping values (0 to 180)
I am having an issue where eulerAngles.y
is returning a Y rotation value that jumps when I move the character head up and down (X axis).
So when I move the head up (above green line), eulerAngles.y
returns 0. But when I move down (below green line), eulerAngles.y
returns 180. Keep in mind that moving the head up and down rotates around the X axis.
Check out the Video (YouTube) explaining the issue.
ou can see in the second picture that the local transform and rotation for the head are not default and are a bit rotated.
The Lines coming from the head are just gizmos created as so:
Gizmos.color = Color.red;
Gizmos.DrawLine(headTransform.position, headTransform.position + headTransform.up*3);
Gizmos.color = Color.green;
Vector3 forward = headTransform.up;
Gizmos.DrawLine(headTransform.position, headTransform.position + (new Vector3(forward.x, 0, forward.z)*3));
I know that `eularAngles` has a rotation order but how do I get the Y rotation in world space that I can apply to any object and it will point in the same direciton.
My goal is to make the body turn to where the head is looking and have the head counteract the body motion so they are aligned. Although I am having other issues, this is just one I am stuck on why this is happening. When I add the code below, the character just spins around and around.
// Make the body rotate to where our head is pointing
bodyTransform.eulerAngles = new Vector3(bodyTransform.eulerAngles.x, headTransform.eulerAngles.y, bodyTransform.eulerAngles.z);
// Counter-act the body by rotating the head back into place
headTransform.eulerAngles = new Vector3(headTransform.eulerAngles.x, bodyTransform.eulerAngles.y, headTransform.eulerAngles.z);
Update:
I tried what @robertbu suggested using `Quaternion.LookRotation` but I am having trouble getting the desired head, body alignment with the below code. I tried messing with the upwards
parameter but nothing works. "Z axis will be aligned with forward and the Y axis with upwards" (Unity documentation) so I am wondering if the transform on the head is messing with it. I am assuming the first part is right because it does turn the body in the direction the head is pointing but I am failing at making the head align with the body to stop it from spinning.
// Make the body rotate to where our head is pointing
/* */
Vector3 headForwardDirection = headTransform.up;
headForwardDirection.y = 0f;
Quaternion qTo = Quaternion.LookRotation(headForwardDirection);
bodyTransform.rotation = qTo;
/* */
// Counter-act the body by rotating the head back into place
/* */
Vector3 bodyForwardDirection = bodyTransform.forward;
bodyForwardDirection.y = 0f;
Quaternion qTo2 = Quaternion.LookRotation(bodyForwardDirection);
headTransform.rotation = qTo2;
/* */
Update #2 (Solution):
Just got the project working as expected with the advice of @robertbu of fixing my model head pivot/axis so that Z points forward and Y up. I added a empty/null and parented the head bone.
Here is the script I used to complete the align. It is a combination of a head look and align when moving.
using UnityEngine;
using System.Collections;
public class HeadMoveScript : MonoBehaviour {
public Transform headTransform; // Used to rotate the head
public Transform bodyTransform; // Used to rotate the body when head rotation limit reached
public Transform testTransform;
public GameObject cMotorGameObject;
public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 }
public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 15F;
public float sensitivityY = 15F;
public float minimumX = -360F;
public float maximumX = 360F;
public float minimumY = -60F;
public float maximumY = 60F;
float rotationX = 0F;
float rotationY = 0F;
Vector3 originalForward;
Vector3 originalUp;
Vector3 originalRight;
Quaternion originalRotation;
void Update ()
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
// Rotate the body when we reach the rotation limits
if (axes == RotationAxes.MouseXAndY || axes == RotationAxes.MouseX)
bodyTransform.localEulerAngles = new Vector3(bodyTransform.localEulerAngles.x, bodyTransform.localEulerAngles.y + ClampExcess(rotationX, minimumX, maximumX), bodyTransform.localEulerAngles.z);
rotationX = Mathf.Clamp(rotationX, minimumX, maximumX);
rotationY = Mathf.Clamp(rotationY, minimumY, maximumY);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, originalUp);
Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, -originalRight);
if (axes == RotationAxes.MouseXAndY)
{
headTransform.localRotation = originalRotation * xQuaternion * yQuaternion;
}
else if (axes == RotationAxes.MouseX)
{
headTransform.localRotation = originalRotation * xQuaternion;
}
else
{
headTransform.localRotation = originalRotation * yQuaternion;
}
/* */
Vector3 moveVelocity = cMotorGameObject.GetComponent<CharacterMotor>().movement.velocity;
// When we start to move forward, make sure we run in the direction our head is pointing
if(Mathf.Abs(moveVelocity.x) > 0 || Mathf.Abs(moveVelocity.z) > 0)
{
// Make the body rotate to where our head is pointing
/* */
Vector3 headForwardDirection = headTransform.forward;
headForwardDirection.y = 0f;
Quaternion qTo = Quaternion.LookRotation(headForwardDirection);
bodyTransform.rotation = qTo;
/* */
// Counter-act the body by rotating the head back into place
/* */
Vector3 bodyForwardDirection = bodyTransform.forward;
bodyForwardDirection.y = 0f;
Quaternion qTo2 = Quaternion.LookRotation(bodyForwardDirection) * Quaternion.AngleAxis(rotationY, -originalRight);
headTransform.rotation = qTo2;
/* */
// Also reset the look rotation of the head
// So we do not jump back after a new loop
rotationX = 0F;
}
/* */
}
void Start()
{
originalForward = headTransform.forward;
originalUp = headTransform.up;
originalRight = headTransform.right;
originalRotation = headTransform.localRotation;
}
void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawLine(headTransform.position, headTransform.position + headTransform.forward*3);
Gizmos.color = Color.green;
Vector3 forward = headTransform.forward;
Gizmos.DrawLine(headTransform.position, headTransform.position + (new Vector3(forward.x, 0, forward.z)*3));
}
// Returns the excess from min or max
float ClampExcess(float value, float min, float max)
{
if(value < min)
return value - min;
else if(value > max)
return value - max;
return 0;
}
}
i think that may be called gimbal lock, maybe google that?
@Anxoss If you like the question, consider upvoting it. It seems that this SE type site lacks voting responses.
Answer by robertbu · Jan 08, 2014 at 10:12 PM
Euler angles are derived from the Quaternion. There are multiple euler angle for any given physical rotation, and Unity may change them on you. Here is a simple example:
transform.eulerAngles = new Vector3(180,0,0);
Debug.Log(transform.eulerAngles);
The values output are (0,180,180). So that means if you were only reading the 'y' value, you would see an immediate 180 degree shift even though the 'y' orientation did not change.
The typical way to solve this problem is to work with either Quaternion.LookRotation() or Transform.LookAt() depending on how you are working with your code. So you would do something like:
Vector3 direction = head.forward;
direction.y = 0.0;
Quaternion qTo = Quaternion.LookRotation(direction);
This takes the forward vector of the head, eliminates any elevation and then calculates a rotation to look in that direction.
To rotate the body over time, to face this direction, you can use Quaternion.RotateToward() or Quaternion.Slerp() depending on whether you want eased movement or not:
transform.rotation = Quaternion.RotateTowards(transform.rotation, qTo, speed * Time.deltaTime);
Check out the update in the OP. I can get some LookRotation
bits working but I can't get the full picture to come together with the body moving to the head rotation and the head moving back so that both have the same rotation. Thanks for the reply!
Why this line?:
Vector3 headForwardDirection = headTransform.up
Is the head model not built correctly? That is, the front of the face should be the forward of the head. If you have rotation (0,0,0), then the face should look at positive 'z'. If this is not true, then all sorts of ugly things can happen. If this is what is going on, then it can be fixed in the modeling program, by using an empty game object, or by using an editor script.
Check this image to see that the y-axis is pointing forward in Unity when using local. It is not the right way in Cinema 4d but the problem is that Unity uses Left hand Y-up Axis and C4D exports as Right hand Y-up Axis (see this thread and my small script that solves it for simple bone rig). I am not using a simple bone rig and ins$$anonymous$$d Cinema 4D Character object which makes it really easy to create biped rigs with both F$$anonymous$$ and I$$anonymous$$ handles but I do not think I can change the pivot/Axis. There is a axis tool in C4D but I can't with this special object/bone.
Edit: I used a empty null around the special bones (in C4D) and zeroed it out so it is correct. I then use that parent null as the normal head transform. I have not gotten the alignment code to work but now we have a good starting point.
Edit #2: I just got it working (see update #2 in op). One of my issues was not resetting the mouse look x rotation. No transition/slerp but the concept is completed.