Preventing rotation of rigidbody at a specific rotation

I have a rigidbody with 4 wheel colliders attached. To get the rigidbody airborne I first add a force to the front of rigidbody then I add a force to the back of the rigidbody. This causes the it to become airborne.

Once the rigidbody has become airborne I want it to stop rotating and remain level to the surface normal below it.

The closest I have come to doing this is to do a raycast downward from the rigidbody to get the hit.normal then monitor the dot product between hit.normal and the rigidbody transform.up and once this value is close to 1 kill all angular velocity.

However since I’m using forces to make the rigidbody airborne the Z axis rotation value is sometimes not level to the ground thus causing the dot product to never be near 1. It normally happens once the board is airborne and it starts to “roll” which is not a desired effect.

I also thought freezing the Z and Y rotations would help stabilize it a bit but I still get some rotation along the Z axis.

I then tried to store the rotation of the rigidbody prior to becoming airborne and rotate it back to that rotation once airborne but I could not get it to rotate smoothly. And it failed in certain cases such as if it became airborne off the side of an incline (the z axis would start off rotated) and the surface it would land on is level.

Here is a little Visual representation of what Im describing as well as some code I currently have. Id appreciate any advise or suggestions. Thanks.

if(controller.OllieInput())
{
	rigidbody.constraints = RigidbodyConstraints.FreezeRotationY |  RigidbodyConstraints.FreezeRotationZ;

	rigidbody.AddForceAtPosition(backForce,backForceLoc.transform.position);

	Invoke ("addingForce",timeBetweeenSecondForce);

	rigidbody.constraints = RigidbodyConstraints.None;

	currentState = PlayerStates.Airborne;
}
void Airborne_FixedUpdate()
{
		
	if(!isBoardLevel)
	{	
		Debug.DrawRay(transform.position,-transform.up);
		RaycastHit hit;

		//sometimes doesnt level off since the z axis rotation is not near 0
		//the force will cause the board to roll a bit 		
		if(Physics.Raycast(transform.position, -transform.up, out hit))
		{
			float dot = Vector3.Dot(transform.up,hit.normal);

			if(dot <= 1  dot >= 0.999)
			{
				Debug.Log ("board is level");
				rigidbody.angularVelocity = Vector3.zero;
				isBoardLevel = true;					
			}
		}
        }    
}

is this side scrolling or full 3d?

Can you slerp to the normal? or does this cause undesired effects?

It is full 3d. I’ve tried to slerp before but it wasn’t doing what I had planned. I think that was due to the to rotation being wrong. I’m not able to access my computer at the moment but I thought about trying something like. Storing the transform.rotation as the board becomes airborne (all wheels are off the ground) then doing the following

transform.rotation = quaternion.Slerp(fromRotation, quaternion.Euler(0, fromRotation.eulerAngles.y,0),time.deltatime);

I don’t know much about quaternion but my though is the to rotation X and y values be 0 so it’s level with floor and the way rotation the same as it was facing. I’ll test this code when i get the chance. If you see any flaws feel free to point them out. :slight_smile:

So after trying some code I’ve finally reached somewhat of a solution. If the dot product between the hit.normal and transform.up is not near 1 then the board needs to keep rotating. So I added the following code all works well except it is a bit jittery when launching off a ramp at an angle. Im not sure how to fix this.

if(Physics.Raycast(transform.position, -transform.up, out hit))
{
	float dot = Vector3.Dot(transform.up,hit.normal);

	if(dot <= 1  dot >= 0.998)//if board is level
	{
		rigidbody.angularVelocity = Vector3.zero;
		
		isBoardLevel = true;
	}
	else//board is not level so check if z axis is level with ground
	{

			float valToBeLearped = Mathf.Lerp(transform.eulerAngles.y, fromRotation.eulerAngles.y,1*Time.time);
			transform.rotation = Quaternion.Euler(transform.eulerAngles.x,valToBeLearped,0);

	}
}