- Home /
why am I getting a flipped-out effect from my transform.rotate
I have a hoverboard that adjusts it's angle depending on the terrain below it. Every so often the board jerks to a strange angle for a moment. My only guess on why is that the raycasts are hitting joining faces of the terrain , And the only solution I can see is somehow logging the last change in angle, if it is an extraordinary leap , then don't transform.rotate What is happening, and what should I do / be looking at ? thanks.
Edit - the problem actually seems to be a Gimbal Lock.
SOLVED with a completely different approach . see answers.
Sample script :
// -- X ANGLE calculations
// vectorBoardLeft
if (Physics.Raycast((transform.position + Vector3((widthRaycastX / 2), 0, 0)), Vector3.down, rayHit)) {
vectorBoardLeft = rayHit.point;
}
// vectorBoardRight
if (Physics.Raycast((transform.position - Vector3((widthRaycastX / 2), 0, 0)), Vector3.down, rayHit)) {
vectorBoardRight = rayHit.point;
}
// calculate X angle
differenceYX = vectorBoardLeft.y - vectorBoardRight.y;
angleBoardXtan = differenceYX / widthRaycastX;
angleBoardRotZ = Mathf.Rad2Deg*Mathf.Atan(angleBoardXtan);
// -- rotate board to align to terrain (Z angle is on the X-axis, and vice verca)
transform.rotation = Quaternion.Euler (angleBoardRotX, 0, angleBoardRotZ);
webplayer publish : http://www.alucardj.net16.net/unityquestions/hoverboard%201-1b.html
full script : http://www.alucardj.net16.net/unityquestions/ScriptHoverBoard2.js
I have just changed the Title of this post as the problem seems to be as described by Kryptos below (thanks for the pointer).
I have just changed the Title of this post as I have inadvertently stirred controversy , and created a conversation that is off-topic . Very sorry to all involved :)
after watching the publish for a few $$anonymous$$utes I notice the huge angle leap occurs around when transform.rotation.x is approximately 0 (zero) . So maybe the problem is the rotation angle (number value) rather than the raycast ? I tried to put a limiter in for near absolute zero in , but it didn't work .
if ((angleBoardRotX < 0.000001)&&(angleBoardRotX > -0.000001)) {angleBoardRotX = 0;}
if ((angleBoardRotZ < 0.000001)&&(angleBoardRotZ > -0.000001)) {angleBoardRotZ = 0;}
You're not multiplying your rotation by Time.deltaTime, so essentially you're letting the object rotate as fast as the frames can be rendered. Not only are you getting a weird angle, but because there's no real speed limit, it's trying to reach that weird angle REALLY fast. If multiplying your rotation by Time.deltaTime doesn't help, you still need some sort of rotation limiter (if conditions stating that it can only rotate if it's not already past certain axial limits).
thanks for the reply . I don't understand where to add the Time.deltaTime , as it Is a 'snap to' (transform.rotation) rather than a slerp (which I don't understand either! http://answers.unity3d.com/questions/230010/rotating-to-the-current-facing-position.html )
$$anonymous$$ultiplying the transform.rotation = Quaternion.Euler would just give weird angles in itself. .. and I am now more convinced that the problem is occuring when the rotation.x is reaching absolute zero (from + or - degrees).
You should never set rotation using euler angles. Otherwise you can reach a narrow case called 'gimbal lock'.
If you want to change the rotation, you should consider calculating a delta rotation and addindg this delta to the current rotation (by combining quaternions).
thankyou , I think you are correct about gimbal lock but I have no idea how to start learning about calculating a delta rotation.
this is part of a bigger project. I wanted to rotate the gameObject (Player) with this method, and move the Player (including the Y-axis) with the following player input methods :
$$anonymous$$oving : http://answers.unity3d.com/questions/230008/capping-the-speed-of-a-rigidbody.html
and Rotating : http://answers.unity3d.com/questions/230010/rotating-to-the-current-facing-position.html
Answer by aldonaletto · Mar 22, 2012 at 04:10 AM
The easiest way is to align the character to the terrain normal, like @Dakwamine suggested - but smoothly, or it will shake a lot.
The script below keeps the alignment relative to the ground. It doesn't take care of movement or gravity, what I suppose you're doing in another script.
private var curNormal: Vector3 = Vector3.up; // smoothed terrain normal private var iniRot: Quaternion; // initial rotation
function Start(){ iniRot = transform.rotation; }
function Update(){ var hit:RaycastHit; if (Physics.Raycast(transform.position, -curNormal, hit)){ // curNormal smoothly follow the terrain normal: curNormal = Vector3.Lerp(curNormal, hit.normal, 4*Time.deltaTime); // calculate rotation to follow curNormal: var rot = Quaternion.FromToRotation(Vector3.up, curNormal); // combine with the initial rotation: transform.rotation = rot * iniRot; } } I answered a question some time ago where a complete CharacterController movement, gravity and terrain alignment were provided by a single script (in C# - click here to see it).
Thanks, am reading up now. Yes, this is part of a bigger project. I wanted to rotate the gameObject (Player) with this method, and move the Player (including the Y-axis) with the following Player Input methods :
$$anonymous$$oving : http://answers.unity3d.com/questions/230008/capping-the-speed-of-a-rigidbody.html
and Rotating : http://answers.unity3d.com/questions/230010/rotating-to-the-current-facing-position.html
I have previously searched and seen your post on 'Orient vehicle to ground normal' , but as it was in C# I didn't try very hard to understand the steps taken. I have enough trouble understanding the commands and classes of UnityScript! (is it java, javascript?). I shall read the other question, break down what the steps are, thx.
I'm having some trouble converting your script. Although I think your vars curDir and turn are what i need for the $$anonymous$$oveRotation in this : http://answers.unity3d.com/questions/230010/rotating-to-the-current-facing-position.html .
This is what I have converted so far, basically my stuck points are the command controller.isGrounded/$$anonymous$$ove , out hit on the raycast ; and % (which remembering from AS2 is the remainder after evenly dividing number by X[360]) ;
converted script so far :
var curDir : float = 0.0;
var vertSpeed : float = 0.0;
var curNormal : Vector3 = Vector3.up;
var turn : float;
var movDir : Vector3;
function Update ()
{
turn = Input.GetAxis("Horizontal") * turnSpeed * 100 * Time.deltaTime;
curDir = (curDir + turn) % 360; // rotate angle modulo 360 according to input //- [ % is ? ]
var hit : RaycastHit;
if (Physics.Raycast(transform.position, -curNormal, out hit)) //- [ out hit ? ]
{
curNormal = Vector3.Lerp(curNormal, hit.normal, 4 * Time.deltaTime);
var grndTilt : Quaternion = Quaternion.FromToRotation(Vector3.up, curNormal);
transform.rotation = grndTilt * Quaternion.Euler(0, curDir, 0);
}
movDir = transform.forward * Input.GetAxis("Vertical") * speed;
// moves the character in horizontal direction (gravity changed!)
if (controller.isGrounded) {vertSpeed = 0;} // zero v speed when grounded //- [ controller.isGrounded ]
vertSpeed -= 9.8f * Time.deltaTime; // apply gravity
movDir.y = vertSpeed; // keep the current vert speed
controller.$$anonymous$$ove(movDir*Time.deltaTime); //- [ controller.$$anonymous$$ove ? ]
}
I am silly sometimes, was so engrossed in your C# answer on the other questions' page, I didn't notice you had already given me a script on this page. It's good to understand rather than copy-pasta hey. The behaviour is working (although infrequently it will 'shake' in the Vector3.forward axis), so thankyou for your answer.
Here is the working test project :
webplayer build : http://www.alucardj.net16.net/unityquestions/hoverboard%201-1d.html
your working script : http://www.alucardj.net16.net/unityquestions/ScriptHoverBoardA1.js
I have to say I am also blown away by how you replaced all my script vars and 4 raycasts with a short script and just 1 raycast. wow.
It's quaternion magic... these damned quaternions are hard to understand, but may save a lot of code (or sometimes drive us nuts too!).
I saw the current webplayer version, and suspect that the jerky forward movement is being caused by lack of synchronicity between the character and the camera. How is the camera following the character? If a separate script moves the camera, it should do it in LateUpdate ins$$anonymous$$d of Update - something like this (camera script):
var target: Transform; // <- drag the player here var distance: float = 8.0;
function LateUpdate(){ transform.position = target.position - distance * transform.forward; }
Your JS version seems ok: only the out keyword must be removed from the Physics.Raycast arguments (JS doesn't use it):
if (Physics.Raycast(transform.position, -curNormal, hit))...I also forgot to include the controller variable: just add this code to the script:
var controller: CharacterController;
function Start(){ controller = GetComponent(CharacterController); }
Answer by Owen-Reynolds · Mar 22, 2012 at 02:00 AM
Atan always gives a "forward" result -- if you're going backwards it gives you exactly 180 degrees flipped as the answer. In other words, as you turn it will tell you 87, 88, 89,90,-89, -88,... . That's not Unity -- it's math.
Most people use Atan2
to solve that -- instead of the slope, you give it y and x (differenceYX and widthRaycast) and it takes care of division by zero and backwards, to give correct 0-360 answers.
thanks for the info, good to know :)
however I did test this, and while Atan2 is returning nice angles, I still have the problem which kind of confirms $$anonymous$$ryptos comment about achieving a Gimbal Lock.
This makes all my degrees vars redundant, I have to find out how to use delta to calculate a rotation (not much success with that search yet).
Here is my updated script from your reply : http://www.alucardj.net16.net/unityquestions/ScriptHoverBoard3a.js
// calculate Z angle
differenceYZ = vectorBoardBack.y - vectorBoardFront.y;
angleBoardZtan = differenceYZ / widthRaycastZ;
//angleBoardRotX = $$anonymous$$athf.Rad2Deg*$$anonymous$$athf.Atan(angleBoardZtan);
angleBoardRotX = $$anonymous$$athf.Rad2Deg*$$anonymous$$athf.Atan2(widthRaycastZ, differenceYZ);
// -- rotate board to align to terrain (Z angle is on the X-axis, and vice verca)
transform.rotation = Quaternion.Euler (-angleBoardRotX + 90, 0, 0);
And the publish with the same effect error : http://www.alucardj.net16.net/unityquestions/hoverboard%201-1c.html