Transform rapidly spins when flipped upside down.

Hello all, I am programming a game where the character is a bug that sticks to walls. The system works perfectly fine, until you go upside down. Once the character climbs to a point where a wall turns into a ceiling (at or very close to 180 degrees in the x axis) the character starts glitching up and rapidly rotating.

The following code has been modified to remove excess stuff, if the additional code is needed, I can paste the full thing of it.

var gravity : Vector3 = Vector3(0,20,0);
var isGrounded: boolean;
var deltaGround: float = 0.2; // character is grounded up to this distance
var PredictionAccuracy : float = 5;
private var myNormal: Vector3; // character normal
private var QNormal : Quaternion;
private var turnAngle: float = 0; // current character direction
var isJumping : boolean = false; // flag "I'm jumping to wall"
private var RotationX = 0.0; 

function FixedUpdate(){

var ray: Ray;
var hit: RaycastHit;

// rotate character to myNormal...

var rot = Quaternion.FromToRotation(Vector3.up, myNormal);
RotationX -= Input.GetAxis("Mouse X") / 15;
var v : Vector3 = Vector3(Mathf.Cos(RotationX),0,Mathf.Sin(RotationX));
var angle : float = Mathf.Atan2(v.x,v.z)*Mathf.Rad2Deg;
turnAngle = angle;
rot *= Quaternion.Euler(0,turnAngle,0); // and to current direction
transform.rotation = rot;
ray = Ray(transform.position, -myNormal); // cast ray downwards
if (Physics.Raycast(ray, hit, (deltaGround-0.1)*2)){ // use it to update myNormal and isGrounded
	isGrounded = hit.distance <= deltaGround;
	if(isGrounded == true){
		QNormal = Quaternion.LookRotation(myNormal);
		QNormal = Quaternion.Slerp(QNormal, Quaternion.LookRotation(hit.normal), Time.deltaTime * 12);
		myNormal = QNormal*Vector3.forward;
	}
}else{
	isGrounded = false;
}

if(isGrounded == false){
	PredictLanding();
	myNormal = QNormal*Vector3.forward;
}
}

}

function PredictLanding(){//The entire point of this function is to chart a trajectory when the character jumps.  It then looks at the normal of the face where it will land and rotates the player so he will land perfectly on it.
	var hit: RaycastHit;
	var ARay = new Ray[PredictionAccuracy];
	var Vel : Vector3 = Vector3(0,0,0);
	for(i = 0; i < PredictionAccuracy; i++){
		var Pos : Vector3 = Vector3((rigidbody.velocity.x*i)/PredictionAccuracy,((rigidbody.velocity.y+Vel.y)*i)/PredictionAccuracy,(rigidbody.velocity.z*i)/PredictionAccuracy);		
		Vel -=gravity/PredictionAccuracy;
		var Pos2 : Vector3 = Vector3((rigidbody.velocity.x*(i+1))/PredictionAccuracy,((rigidbody.velocity.y+Vel.y)*(i+1))/PredictionAccuracy,(rigidbody.velocity.z*(i+1))/PredictionAccuracy);
		Debug.DrawLine(transform.position + Pos,transform.position + Pos2);
		ARay *= Ray(transform.position+Pos, Pos2);*

if (Physics.Raycast(ARay_, hit, (rigidbody.velocity.magnitude*(deltaGround-0.1)2)/PredictionAccuracy)){ // use it to update myNormal and isGrounded_
_
QNormal = Quaternion.LookRotation(myNormal);_
_ QNormal = Quaternion.Slerp(QNormal, Quaternion.LookRotation(hit.normal), Time.deltaTime * 4);_
_
return;_
_
}else {_
_
QNormal = Quaternion.LookRotation(myNormal);_
_ QNormal = Quaternion.Slerp(QNormal, Quaternion.LookRotation(Vector3.up), Time.deltaTime * 4);_
_
}_
_
}_
_
}*_
I have tried changing the Quaternion.Euler values to other things but to no avail. After looking at the rotation values in the inspector, it looks like a combination of all 3 rotations are rapidly spazzing out as soon as it flips totally (or near totally) upside down. The glitch is incredibly disruptive because it limits me to designing courses that never go upside down, which is really a downer in a game that relies on sticking to walls. Some more information on the glitch is that the character seems drawn to go downwards. If you are on a sphere and position yourself and your angle such that you are going perfectly alone the equator of the sphere, it will quickly turn you if you go forward to draw you to the bottom. And the problem is, as soon as you reach the bottom of the sphere, I assume it is trying to continue downward when you are already upside down, so it just flips out. Any advice on how to fix the issue?

I managed to solve it. Although the character rotates along the world z axis when he jumps between opposite walls (a trait I find undesirable), he doesn’t freak out when he is on the bottom of something.

Instead of using Quaternion.FromToRotation() I decided to create a point coming off of the normal and find the angle to reach it and use that.

	var E : Vector3 = transform.InverseTransformPoint(transform.position + myNormal);
	var anglex : float = Mathf.Atan2(E.z,E.y)*Mathf.Rad2Deg;
	transform.Rotate(anglex,0,0);
	
	E = transform.InverseTransformPoint(transform.position + myNormal);
	
	var anglez : float = -Mathf.Atan2(E.x,E.y)*Mathf.Rad2Deg;
	transform.Rotate(0,0,anglez);

	RotationX = Input.GetAxis("Mouse X") * 8;
	
	transform.Rotate(0,RotationX,0);

This solves the issue of flipping out like a maniac quite well. If anyone has any ideas on how to keep him from rotating along the world z axis when he jumps between walls, and instead use his relative z axis, please tell me.

I’m thinking the problem is the extra degree of freedom in FromToRotation.

Imagine the normal is sticking out forwards and to the right (so the slope ahead is angled down and to the right.) In your mind, Q.FtoR should tilt you forwards (around x) then sideways (around z.) Technically, once you are lined up, the extra y-spin could be anything. But, it shouldn’t add extra, so should have 0 y-spin and basically be facing forwards.

BUT, it might be quicker to first spin around y, to face the normal, then spin forwards on local x. In that case, the model is facing rightish. It’s really worst than that – quaternions aren’t x,y,z spins and don’t even understand the concept of “not changing the y-spin.”

This snippet of test code (yours, stripped down) shows the problem (floor is a plane: tilt the plane in scene view. As it tilts more sideways, the facing squirms around on us as a y-spin becomes better. At nearly upside down, it is stable, but tiny changes affect y-spin):

public Transform floor;
public float spin = 0;

void FixedUpdate () {
  Vector3 norm = floor.up;
  Quaternion QQ = Quaternion.FromToRotation(Vector3.up, norm);
  transform.rotation = QQ;
  transform.position = floor.position + floor.up*10;

  if(Input.GetKey("left")) spin-=0.1f; // no problems with this spin,
  if(Input.GetKey("right")) spin+=0.1f; // except based off messed-up
  Quaternion RR = Quaternion.Euler(0,spin,0); // normal spin
  transform.rotation *= RR;
}

Since we are tilting from Vector3.up, the best tilt tends to twist y slightly towards the normal, which is “down.”

I would take advantage of that mystical “best combo of x,y,z” process. Instead of using Quat.FromtoRot(up,norm), use Quat.FromToRot(trans.up, norm). That will give the best flip for the bug. So, if you are leaning a bit forwards and jump to the roof, you’ll finish the summersault. If you “pop a wheelie” and then jump to the roof, you’ll complete the rest of it. If you start yourself leaning to the left and jump, the “best” spin will be to keep spinning left and land on the ceiling facing forwards. In theory, learning to “start the roll” the way you want might actually be fun.

I think you’d have to redo turnangle as a change to current, instead of an absolute, if you did that.