Lerping Quaternions

Hi guys,

I’m having some trouble making my object lerp correctly with rotation. I know there’s some funky business using quaternions an euler angles. I’m not sure what the problem is though, as my translation for lerp works well enough.

public class PlayerScript : MonoBehaviour {
	
	int playerHealth = 100;
	float reposStart, furthestDist, furthestRot;
	public float collisionDelay = 0.7f, reposSpeed = 1, moonTapped;
	Vector3 savedPos, savedRot;
	public bool bMoveAbility;
	
	// Use this for initialization
	void Start ()
	{
		// Setting the Civilisation's original position.
		savedPos = transform.position;
		bMoveAbility = false;
		savedRot = transform.eulerAngles;
	}
	
	//Called from the 'MoonScript'
	public void LoseHealth(int healthLost)
	{
		// gives a delay before re-aligning the civilisation's position, and sets the start time for reposition as well.
		reposStart = Time.time;
		moonTapped = collisionDelay;
	}

	// This puts the civilisation's back in their neutral position after being hit..
	void FixedUpdate()
	{
		// Gives the Civilsations a delay before they reposition
		if(moonTapped > 0)
		{
			// If timer's still higher than requirement, minus time from it.
			moonTapped -= Time.deltaTime;
			
			// Gets the furthest distance/rotation travelled before the delay expires.
			furthestDist = Vector3.Distance(savedPos, transform.position);
			furthestRot = Vector3.Dot(savedRot, transform.eulerAngles);
			
			// Don't let it recalibrate if not ready. Return that shit.
			return;
		}
		
		if(!bMoveAbility)
		{
			// Takes the time travelled divided by the total journey left, to get the fraction of the journey travelled.
			float timePosCovered = (Time.time - reposStart) * reposSpeed;
			float timeRotCovered = (Time.time - reposStart) * (reposSpeed + 2);
			
			float fractureDistJourney = timePosCovered / furthestDist;
			float fractureRotJourney = timeRotCovered / furthestRot;
			
			// Repositions the well.. position.
			if(savedPos != transform.position)
			{
				Vector3 currPos = transform.position;
				
				transform.position = Vector3.Lerp(currPos, savedPos, fractureDistJourney);
			}
			
			// Repositions the rotation.
			if(savedRot != transform.eulerAngles)
			{
				Vector3 newRot = Vector3.Lerp(transform.eulerAngles, savedRot, fractureRotJourney);
				
				Quaternion rotation = Quaternion.Euler(newRot);
				
				transform.rotation = rotation;
			}
		}
	}

As I said. Translation works absolutely okay. I have an issue with getting the rotation to work. Instead of smoothing to the correct rotation naturally. It jolts there immediately, as if there is no time difference between beginning and end of rotation.

Can you help?

Thanks

I understand to make the code more efficient I could turn this into a coroutine so I don’t constantly have if statements running in the main game loop, but I will still be coming across this problem.

Can anyone please help?

Thanks

Why not use Quaternion.Lerp directly instead of converting eulerAngles and Vectors?

Main Reason: I’ve only had Unity a month and don’t know which way is up yet :stuck_out_tongue: lol.

Okay I’ll give that a go and post up on here if I get any problems.

Thanks mate

You could also try Quaternion.Slerp. It works better for rotations. There is also Mathf.LerpAngle.

Too many options! What’s the best route for this?

Also so I know how to make this work… What was I doing wrong? Unless I actually know what I was doing wrong I’ll possibly do it wrong any other way…

Okay I’ve changed it to a coroutine and I’m getting even stranger results…

It’ll immediately flip back to it’s position after the delay… And then fly off again.

public class PlayerScript : MonoBehaviour {
	
	int playerHealth = 100;
	float reposStart, furthestDist, furthestRot;
	public float collisionDelay = 0.7f, reposSpeed = 1;
	Vector3 savedPos, savedRot;
	public bool bMoveAbility;
	
	// Use this for initialization
	void Start ()
	{
		// Setting the Civilisation's original position.
		savedPos = transform.position;
		bMoveAbility = false;
		savedRot = transform.eulerAngles;
	}
	
	//Called from the 'MoonScript'
	public void LoseHealth(int healthLost)
	{

		// Passed health lost from the magnitude of the velocity from the moon
		playerHealth -= healthLost;
		
		StartCoroutine(ColonyRepos() );
	}
	
	IEnumerator ColonyRepos()
	{
		yield return new WaitForSeconds(collisionDelay);
		
		furthestDist = Vector3.Distance(savedPos, transform.position);
		
		furthestRot = Vector3.Dot(savedRot, transform.eulerAngles);
		
		reposStart = Time.time;
		
		while(savedPos != transform.position  savedRot != transform.eulerAngles)
		{
			float timePosCovered = (Time.time - reposStart) * reposSpeed;
			float timeRotCovered = (Time.time - reposStart) * (reposSpeed + 2);
			
			float fractureDistJourney = timePosCovered / furthestDist;
			float fractureRotJourney = timeRotCovered / furthestRot;
			
			Vector3 currPos = transform.position;
			transform.position = Vector3.Lerp(currPos, savedPos, fractureDistJourney);
			
			Vector3 newRot = Vector3.Lerp(transform.eulerAngles, savedRot, fractureRotJourney);
			Quaternion rotation = Quaternion.Euler(newRot);
			transform.rotation = rotation;
			
			yield return null;
		}
	}

Also using Quaternion.Lerp and Quaternion.Slerp gives me the same results. Immediately goes back to position:

			if(savedRot != transform.eulerAngles)
			{
				//Vector3 newRot = Vector3.Lerp(transform.eulerAngles, savedRot, fractureRotJourney);
				
				Quaternion rotation = Quaternion.Euler(savedRot);
				
				//transform.rotation = rotation;
				transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.time * 1);
			}
			// Repositions the rotation.
			if(savedRot != transform.eulerAngles)
			{
				//Vector3 newRot = Vector3.Lerp(transform.eulerAngles, savedRot, fractureRotJourney);
				
				Quaternion rotation = Quaternion.Euler(savedRot);
				
				//transform.rotation = rotation;
				transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.time * 1);
			}
			// Repositions the rotation.
			if(savedRot != transform.eulerAngles)
			{
				//Vector3 newRot = Vector3.Lerp(transform.eulerAngles, savedRot, fractureRotJourney);
				
				Quaternion rotation = Quaternion.Euler(savedRot);
				
				//transform.rotation = rotation;
				transform.rotation = Quaternion.Slerp(transform.rotation, rotation, fractureRotJourney);
			}

It just flips immediately back to it’s original position with no change over time. What is happening?

First thing:

 Quaternion Lerp(Quaternion from, Quaternion to, float t);

Don’t use the current rotation in the ‘from’ parameter. The from is the starting point before you do any rotation and the to is the end point after all rotation is done.

Second thing:

 transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.time * 1)

The float t is decievingly named; its not time. It’s the percentage, from 0 to 1, you are along path. So you want something more like:

void Update()
{
    Percentage = (Time.time - StartTime) / LengthOfSlerpSeconds;
    transform.rotation = Quaternion.Slerp(StartingRotation, EndRotation, Percentage);
}

Awesome Mycroft thank you. I now understand that completely. However the code is still on prozak. I now know the exact fault. The while loop turns into a endless loop. The reason is because the object never loses its velocity even though I’m changing his transform in the while loop.

If I force the objects while loop to end after a period of time, it’s exact previous velicty from the hit makes the object continue off into the distance.

This is what I’m using. So I’m assuming I need to give up on Lerp entirely and use velocity instead?

	IEnumerator ColonyRepos()
	{
		yield return new WaitForSeconds(collisionDelay);
		
		furthestDist = Vector3.Distance(savedPos, transform.position);
		furthestRot = Vector3.Dot(savedRot, transform.eulerAngles);
		
		startTime = Time.time;
		
		Vector3 distFromOrigin = transform.position;
		Quaternion rotFromOrigin = transform.rotation;
		
		Debug.Log("furthestDist"+furthestDist);
		
		while(Vector3.Distance(savedPos, transform.position) >= 1) //|| savedRot != transform.eulerAngles)
		{
			float dist = Vector3.Distance(savedPos, transform.position);
			Debug.Log("Still going = "+ dist);
			
			float timePosCovered = (Time.time - startTime) * reposSpeed;
			
			float fractureDistJourney = timePosCovered / furthestDist;
			
			transform.rigidbody.transform.position = Vector3.Lerp(distFromOrigin, savedPos, timePosCovered);

			Quaternion rotation = Quaternion.Euler(savedRot);
			transform.rigidbody.transform.rotation = Quaternion.Lerp(rotFromOrigin, rotation, timePosCovered);
			
			yield return new WaitForFixedUpdate();
		}
		
		Debug.Log("Reached End");
	}