- Home /
Quaternion and gravity trouble
Hi folks,
I'm trying to do a custom gravity setup to work with characters moving on small planets. Unfortunately I have some trouble with how the transform moves when it's close to 100% downwards (0,-1,0).
Around that spot, the character starts to turn in circle really quickly. But when I cross it straightly coming forward from the top I have no problem going through it.
Probably a misunderstanding of quaternions... but I've been doodling around for a while now!. A fresh look would help and be super appreciated ;)
Cheers,
Vincent
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GravityPlayerController : MonoBehaviour {
// Movement
private Rigidbody myBody;
private Transform myTransform;
private Vector3 moveDirection;
private Vector3 gravityUp;
private Vector3 worldUp = new Vector3(0, 1, 0);
public float moveSpeed = 1f;
public float gravityForce;
public GravitySource gravitySource;
// Inputs
private float moveFrontBack, moveLeftRight;
private float mouseX, mouseY;
public float mouseSensitivity = 10f;
public Text debugText;
// Other objects
public GameObject target;
void Start()
{
myTransform = transform;
myBody = GetComponent<Rigidbody>();
gravityForce = gravitySource.gravityForce;
}
void Update()
{
mouseX += Input.GetAxis("Mouse X");
gravityUp = (myTransform.position - gravitySource.transform.position).normalized;
moveDirection.Set(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Debugging();
}
void FixedUpdate()
{
myBody.MovePosition(myTransform.position
+ myTransform.TransformDirection(moveDirection)
* moveSpeed);
myBody.MoveRotation(Quaternion.FromToRotation(worldUp, gravityUp)
* Quaternion.Euler(0, mouseX, 0));
myBody.AddForce(gravityUp * gravityForce);
}
void Debugging()
{
debugText.text = "Rotation = " + myTransform.rotation.ToString("F6") + "\n"
+ "Position = " + myTransform.position.ToString("F6");
}
}
public class GravitySource : MonoBehaviour {
public float gravityForce = -9.8f;
}
I imagine it must be linked to that kind of problem, I was aware of its existence but actually never looked it up... I guess time has come!
the problem is in storing the mouse x as an angle and then using euler angles, but on a sphere there is no way to have a "forward" direction that is the same everywhere so it has to have at least one pole. To see what I mean check out this article https://en.wikipedia.org/wiki/Hairy_ball_theorem
I was thinking that maybe I could let the gravity vector handle X and Z rotation and then add the mouse value on the Y component of the localRotation of the character.
Do you think it might have a different outcome?
Also I tested with removing the mouse Quaternion, and still had an erratic behavior around the south pole. I thought that maybe the rotation of the gravityUp vector could be in cause.
gravityUp = (myTransform.position - gravitySource.transform.position).normalized;
[...]
myBody.$$anonymous$$oveRotation(Quaternion.FromToRotation(worldUp, gravityUp)
Answer by MrElemental · Aug 16, 2017 at 11:39 AM
Hi folks,
I've been screwing my head off trying to get deeper into the magic of quaternions and finally deduced that the less you play directly with quaternions, the better.
Looking at this video on YouTube, I realized that, as a newcomer to Unity and game dev, I'd be better off using functions generating and manipulating quaternions, rather than trying to steal the wizard's hat to enchant brooms.
In the end this code worked out perfectly.
Cheers, and thanks for your replies!
void Start()
{
myTransform = transform;
myBody = GetComponent<Rigidbody>();
gravityForce = gravitySource.gravityForce;
myBody.useGravity = false;
myBody.constraints = RigidbodyConstraints.FreezeRotation;
}
void FixedUpdate()
{
// Movement
moveDirection.Set(Input.GetAxisRaw("Horizontal"),
0,
Input.GetAxisRaw("Vertical"));
moveAmountTarget = moveDirection.normalized * moveSpeed;
moveAmount = Vector3.SmoothDamp(moveAmount,
moveAmountTarget,
ref smoothMoveVelocity,
.15f);
myBody.MovePosition(myBody.position + myTransform.TransformDirection(moveAmount));
// Gravity rotation
gravityVector = (myTransform.position - gravitySource.transform.position).normalized;
myTransform.Rotate(Vector3.up * Input.GetAxis("Mouse X"));
myTransform.rotation = Quaternion.FromToRotation(myTransform.up, gravityVector)
* myTransform.rotation;
// Apply gravity
myBody.AddForce(gravityVector * gravityForce);
}
Answer by NorthStar79 · Aug 14, 2017 at 12:27 PM
myBody.MoveRotation(Quaternion.FromToRotation(worldUp, gravityUp) * Quaternion.Euler(0, mouseX, 0));
this
Quaternion.Euler(0, mouseX, 0));
part will cause that kind of problems most of the times, and it's so hard to wrap it your head around.
once i had same issue, and i solve it by saving last "mouseX" position from previous frame, and check if its changed. if its not changed at all , dont use it at all, if its changed lite bit you are okay, if its change rate is huge, just lerp it, dont try to change that rotation all at once in one frame.
i hope this will help.
Your answer

Follow this Question
Related Questions
Difficulty making a complex camera rotation 2 Answers
Some Issue With Quaternions Short Way 0 Answers
How to rotate object based on another rotation. 1 Answer
How to use quaternions to apply an offset to a rotation to calibrate a controller 1 Answer
Unity.Mathematics equivalent to Quaternion.FromToRotation? 3 Answers