- Home /
Why is rotation locking to an axis?
I have a 3rd person skateboarding character (imagine Tony Hawks) whose rotation I wish to set at the top of a half pipe (so that they launch straight "up" and land back down on the pipe, without moving either forward or backwards). I'm doing this as, for small half pipes, some forward momentum is maintained so they hit the top and keep moving forward. Not ideal!
Fig 1 - Skateboarder and halfpipe.
In order to set the correct rotation (as if the player is riding an invisible wall aligned with the half pipe), I figure the players local x rotation should be 270.
Fig 2 - Halfpipe with "wall". No matter the angle player goes up halfpipe, they should behave as if riding this (nonexistant) wall
To do this, I have a collider/trigger set up at the lip of the pipe, with an OnTriggerEnter() calling this code:
if (coll.gameObject.tag == "Player")
{
Vector3 newEulerAngles = new Vector3(270,
coll.gameObject.transform.rotation.eulerAngles.y,
coll.gameObject.transform.rotation.eulerAngles.z);
float speed = 100000; // want this to be instant
float step = speed * Time.deltaTime;
coll.gameObject.transform.rotation =
Quaternion.RotateTowards(coll.gameObject.transform.rotation,
Quaternion.Euler(newEulerAngles), step);
}
When trying this however, the y and z rotation is set to zero, every time.
Fig 3 - Approaching pipe at an angle Fig 3 - On trigger, player rotation is snapped to (270, 0, 0), despite only x being altered
I've tried various methods and they all behave this way, for example the simpler script:
if (coll.gameObject.tag == "Player")
{
Vector3 newEulerAngles = new Vector3(270,
coll.gameObject.transform.rotation.eulerAngles.y,
coll.gameObject.transform.rotation.eulerAngles.z);
coll.gameObject.transform.rotation = Quaternion.Euler(newEulerAngles);
}
I've done some looking around and my best guess is this has something to do with Gimbal locking, but I could be way off. If anyone can point out why the player is snapping to this angle and ignoring the previous y, z values I'd love to learn!
Answer by SniperEvan · Oct 24, 2015 at 06:22 AM
Could coll.gameObject.transform.rotation.eulerAngles.y be getting set to 0 earlier in the script? Try printing that value without doing anything else and let me know what it says.
I would definitely try print (the xyz euler values) on the initial rotation and the created quaternion to see what the heck is going on.
Rotations in unity have given me so much frustration in the past. Hope that helps a little tho. Let me know what you find out and maybe I can shed some more light on what's happening.
Answer by Bunny83 · Oct 24, 2015 at 07:10 AM
You experience a Gimbal Lock since you work with an eulerangles representation. Unity specifically uses Quaternions to avoid a gimbal lock. However when you use eulerAngles you convert the rotation to an eulerAngles representation.
You shouldn't use any absolute values and avoid using euler angles in such cases.
This is not something that Unity is responsible for. This is a pure effect / limitation of an eulerangles representation.
Hi Bunny83, thanks for the answer. Have you any suggestions on how to set the required angle without using euler angles? This has me totally stumped.
Don't rely on angles. Either use relative rotations or direction vectors to create quaternions. Quaternion.LookRotation for example creates a rotation along a forward axis and aligns the rotation around that axis based on a given up vector.
You might want to use the normal vector of the surface you want to align your object to. You can pass transform.forward as look vector and the ground normal as up vector. However it's difficult to give a specific solution since we know almost nothing about your game dynamics.
Answer by toromano · Oct 24, 2015 at 10:57 AM
First of all, if you need to lerp a rotation, you should not do this in OnTriggerEnter() method. Because this method is just called once when you enter. you should do lerping in OnTriggerStay() method.
What i would do is: First i would get the rotation at first enter
Quaternion enterRotation;
OnTriggerEnter()
{
enterRotation = transform.rotation;
}
Then while i am in trigger i would lerp that rotation
OnTriggerStay()
{
//lets say you need 30 degree rotation around your transform's local up axis (since pivot may vary with .fbx files, you might need to use transform.right or transform.forward):
Quaternion targetRotation = enterRotation * Quaternion.AngleAxis(30, transform.up);
transform.rotation = Quaternion.Lerp(transform.rotation,targetRotation,Time.fixedTime * speed)
}
I hope that works for you. There might be typo errors since i don't have a compiler right now. Sorry for that.
Hi toromano,
I was using enter as I want to instantly snap the player to the correct rotation. I'm doing this for simplicity, but yeah I'll keep in $$anonymous$$d the trigger stay for when I bump it up to lerping.
I'll give your stuff a go and see how it turns out, thanks!