Gimbal lock without rotation WTF?

Hello!

I’m testing unity (with a sonic like game) to see how far I can go with action game and I met some strange problem. I have a gimbal lock when my character is running upside down. The strangeness is that it’s all vector based (alignment and interpolation), not a single euler rotation is used.

Basically what I do is to apply a force along local coordinate (a transform.translate) and aligning the character to collision normal so it stay on ground (think mario galaxy).

here is an image:

The character is on a sphere. The blue trail show the path it takes. The red square show the local coordinate in world space (direct reading no conversion), the thin dark blue line show the force applied. The pale square show the contact with the ground and the cyan line show the current direction of the character (the force converted into world space).

The character is upside down, the dark blue line and the cyan line should be opposite, instead the cyan line show that the character is circling around the pole (the trail show various circle), the code does not return turn so the character is moving straight ahead in code term. If the character land on the pole it start to spin furiously.

Other problem I have that maybe is related is that the character does not jump relative to slope that are more than 90° (upside down) and camera shift upside down when in follow mode.

I have check thread about quaternion and 6dof but find nothing relevant.

You can check a build here with source code:
http://dl.dropbox.com/u/24530447/build.rar
The code is older (without transform.translate as a result of checking alternative, it use a custom moveEntity) but the result is exactly the same.

Mice control the camera around the character, left click jump, right click stop the character, left+right enter spindash mode, releasing them dash the character forward.
Set up direction with unity’s key configuration (qsdz otherwise as an azerty mapping).
The code is based on the damizean blitzengine (disclaimer inside source)

How I can solve that? Almost any project I have involve some sort of upside down magic!

I have isolate the faulty (?) piece of code to show you the matter

The code who align and move the character

			this.Align();

			//adapt to character controller
			MoveController=new Vector3 (
				this.Motion_Speed.x * Time.deltaTime,
				this.Motion_Speed.y * Time.deltaTime - (0.015f/5f+ (this.Motion_Speed.magnitude * 0.0000033f * Time.deltaTime)),//magnet to ground
				this.Motion_Speed.z * Time.deltaTime);

			this.body.transform.Translate(MoveController,Space.Self);

The code who align the character

//inside the testcollision method
...
CollisionNormal = contact.normal;
...
this.Motion_Align = GroundNormal;
...




void Align ()
	{
		this.AlignToVectorQuaternion (body, this.Motion_Align, "Y", 1f);
	}





public void AlignToVectorQuaternion(Rigidbody GO, Vector3 Vect, string Axis, float Rate)
	{
		this.AlignToVect_Quaternion(GO.transform,Vect, Axis, Rate);
	}




void AlignToVect_Quaternion(Transform Trans,Vector3 Vect, string Axis, float Rate)
	{
		Vector3 AlignAxis = Vector3.zero;

		switch (Axis)
		{
		case "X":
			AlignAxis = Vector3.right;
			break;

		case "Y":
			AlignAxis = Vector3.up;
			break;

		case "Z":
			AlignAxis = Vector3.forward;
			break;
		}
		
		Trans.transform.rotation = 
			Quaternion.Lerp(
				Trans.transform.rotation,//from
				Quaternion.FromToRotation(AlignAxis,Vect),//to this
				Rate//at this rate
				);
	}

I can’t think of simpler code than that!

How can I solve this or create manual code (6dof movement) to avoid that?

I only skimmed the code, but this:

		Trans.transform.rotation = 
			Quaternion.Lerp(
				Trans.transform.rotation,//from
				Quaternion.FromToRotation(AlignAxis,Vect),//to this
				Rate//at this rate
				);

Look somewhat suspicious. I’m not sure what the input vectors to FromToRotation() represent in your code, but FromToRotation() is usually used to generate a relative (rather than absolute) rotation, and as such, interpolating to its return value as in the above code seems unlikely to produce the desired results.

Again though, I’m not sure that that’s the problem - it’s just something I noticed.

What it does is that it takes the current orientation of one axis the GO (AlignAxis) and then compute the desired alignment (Motion Align pass through Vect). The behavior is the same without the Lerp or even if I use euler based rotation or use transformDirection to translate it in world space.

What the code does is it takes all collision normal, process them into ceiling, wall and ground collision (all relative to local coordinate), take the computed GroundNormal (which is local) put it in Motion_Align (member of the class) which is use everywhere for animation and controlling motion relative to alignment.

The problem have been found on the tigsource forums:

Wich allow me to understand this Unity Answer topic I missed:
http://answers.unity3d.com/questions/1363/how-to-get-quaternion-fromtorotation-and-hit-normal-to-allow-the-y-axis-to-not-be

adaptation to my code

//adapt to character controller
MoveController = new Vector3 (
	this.Motion_Speed.x * Time.deltaTime,
	this.Motion_Speed.y * Time.deltaTime - (0.015f/5f
+ (this.Motion_Speed.magnitude * 0.0000033f * Time.deltaTime)),//magnet to ground
	this.Motion_Speed.z * Time.deltaTime);

	Vector3 fwd=this.transform.forward;
	Vector3 proj = fwd - (Vector3.Dot (fwd, this.Motion_Align)) * this.Motion_Align;
	this.transform.rotation = Quaternion.LookRotation(proj, this.Motion_Align);

	this.body.transform.Translate(MoveController,Space.Self);

And it work for the specific problem I asked for but came with it’s own problem!
Now when I move from vertical to horizontal at 90° the GO is misalign and control are fucked up (it may happen in other case, but have been verified for this one). It correctly align after a jump (reset to default orientation) because jumping use the old code.

If you may help me for those problem, I need to realign without messing what have been done. Currently it’s simple the GO is always align to world space when Y is up. in -Y he is simply the inverse but perfectly align. I need to keep this alignment no matter what, control are generally tangential to its “rotation” except jump.

I guess Quaternion and using vector does not protect from Gimbal lock after all.

It didn’t solve the jump from upside down sloped too, they are still not working at all. I have put extra flags to test the code, a sticky flag so the character does not fall when speed is too low from ceiling and a galaxy flag that does not realign the character when he jump. With the galaxy flag (without realign), slope operate normally I guess it’s a problem of the character reattaching to the ground and have no clue why currently.

Basically I realize I’m making a mario galaxy/spider man like type of control. Moving correctly on arbitrary mesh. If i succeed emulating that, I will release the code for free to the community (along with a free demo game). I think it will be a very versatile code to do ambitious platformer or other type of full 3D navigation.