Using Quaternion.LookRotation( ) on y axis only, but keep x axis of transform the same?

The problem I have is a little different than what I have read about concerning the same issue. I found plenty of answers showing how to only rotate on 1 axis when using functions like ‘Quaternion.LookRotation()’, and I can achieve that just fine. My problem is that the object I am rotating towards the target is a motorcycle, which also rotates on the X axis for forward/backward pitch.

When I limit the rotation to the y axis only, and zero out the other 2, my motorcycle rotates towards the target on Y just fine, but it’s X axis rotation is now locked to 0…so when going up-hill or down-hill, the bike doesn’t rotate anymore.

If I don’t zero out the X axis for the rotation, the the bike’s X axis rotation is also following the target, so if my bike is going up-hill and the next waypoint is at y = 0 (on the ground), then the bike’s X rotation tries to point towards the waypoint and rotates the opposite way of the up-hill, down-hill.

I have hit a roadblock trying to figure out how to leave the bike’s X rotation alone while making its Y axis follow waypoints. Here’s what I have so far:

void Update () 
{
    // Loop through waypoints
	if( currentWaypoint < waypoints.Length )
	{
	    // Get direction of next waypoint
	    Vector3 direction = waypoints[currentWaypoint].position - transform.position;
			
	    // Check if we passed the current waypoint and get the next one if we did
	    if( transform.position.x > waypoints[currentWaypoint].position.x )
	    {
	        currentWaypoint++;
	    }
	    else
	    { 
            Quaternion newRotation = Quaternion.LookRotation( direction );
	        Quaternion rot = Quaternion.Slerp( transform.rotation, newRotation, 
	        transform.parent.rigidbody.velocity.magnitude * Time.deltaTime );
				
            transform.rotation = rot;

            // This version below gives me the bike's X rotation bike, but I gimbal lock problems...
            //transform.rotation = Quaternion.Euler( transform.eulerAngles.x, rot.eulerAngles.y, 0.0f );	
	    }
	}
	else
	{
	    Debug.LogWarning("NO MORE WAYPOINTS TO FOLLOW");
	}
}

Quaternions give me a headache :slight_smile: I’ve been trying to get this to work for the past week, and although I am really close, it’s still not stable enough to be used.

If any of you rotation gurus out there can give me some much needed help, I would really appreciate it! Thanks for your time,

Stephane

Usually you should use a parent/child hierarchy: the parent rotates around the Y axis and the child rotates locally around X - doing both rotations with the same object is complicated, because the first rotation also rotates the axes, and the second rotation gets tilted.

But in your specific case - a two wheel vehicle - there’s a simpler and smarter solution: find the horizontal direction and cast two rays to the ground, one ahead and the other behind the bike; define a vector from back hit point to forward hit point and assign it to transform.forward:

  ...
  else {
    RaycastHit hitFwd;
    Physics.Raycast(transform.position + direction.normalized, -Vector3.up, out hitFwd);
    RaycastHit hitBck;
    Physics.Raycast(transform.position - direction.normalized, -Vector3.up, out hitBck);
    Vector3 dirXY = (hitFwd - hitBck).normalized;
    float speed = transform.parent.rigidbody.velocity.magnitude;
    transform.forward = Vector3.Lerp(transform.forward, dirXY, speed * Time.deltaTime);
  }
  ...

NOTE: this algorithm was suggested by @SirGive in the question Orient vehicle to ground normal (terrain hugging) - Questions & Answers - Unity Discussions

I got the rotation to work the way I needed it to work, so for future reference here it is below (I tried both solutions given above with no success for my particular situation, but thanks anyways guys):

// Get direction of next waypoint
Vector3 direction = waypoints[currentWaypoint].position - transform.position;
			
// Check if we passed the current waypoint and get the next one if we did
if( transform.position.x > waypoints[currentWaypoint].position.x )
{
    currentWaypoint++;
}
else
{ 
    Quaternion newRotation = Quaternion.LookRotation( direction.normalized );
    float yAngle = Mathf.LerpAngle( transform.eulerAngles.y,newRotation.eulerAngles.y - 90, rBody.velocity.magnitude * Time.deltaTime );
				
    rigidbody.MoveRotation( Quaternion.Euler( 0, yAngle, transform.eulerAngles.z ));
}

Hopefully this can help anyone else in the same situation :slight_smile:

Stephane