Constraining Rigidbody to Spline

Hi,

I have a basic car setup with a rigidbody and four wheel colliders. I want the car to move forward and jump whilst being constrained to a spline created with super spline. I have managed to get various different solutions almost working but always run into problems. The car drifts away from the spline as it travels around corners at speed. I have tried adding forces to move it back towards the spline but nothing works quite right.

Any advice on a setup would be appreciated.

Thanks

Sounds like you have two systems fighting each other. Unity’s physics will try to make the car behave naturally, while your spline constraints are trying to keep it on the track no matter what. I’d set it up like this:

  • give the car a script that makes it
    steer towards a target as if the
    player was driving it (eg. feed fake
    input axes into it which turn the wheels), under normal physics.

  • have another script find the nearest point to the car on the track spline. Then look X units ahead along the spline, and feed that position into the steering script as a target.

This will make the car drive naturally and self-correct onto the track even if pushed off by the player. Expose some floats to control how hard the car turns and accelerates towards the target, and how far ahead of the car the target is, and you should be able to tweak it to navigate the track pretty well.

Note: it would be easier to just move the target along the spline at some fixed speed, but that will go wrong if the car is slowed down or catches up on a long straight. If you always find the nearest point and look ahead from that, it will work no matter how much the car’s speed varies.

Further note: finding the nearest point on the spline is quite complex, and can go wrong if the spline crosses itself (eg. a figure eight) or nearly does. The mathematical solution for a Bezier spline is a 6th-order differential equation, which is too hardcore for me, so I recommend sampling the spline repeatedly to find the closest point. Shout if you need further help - there’s lots of ways to optimize that search.

Thanks Winterblood for all your help and advice. In the end I have got a solution that seems to work pretty well. I have included the main functions that are involved with the movement and position of the rigidbody and also FixedUpdate() as the order in which the functions are called proves to be important.

void FixedUpdate()
{
	UpdateSplinePositionAndRotation();
	HandcarRotation();
	SetToSplinePosition();
	WheelTorque();
}

void UpdateSplinePositionAndRotation()
{
	//fetch and store the spline position and rotation for other functions to use for optimisation
	float splinePoint = spline.GetClosestPoint(myTransform.position, splineAccuracy);
	splinePosition = spline.GetPositionOnSpline(splinePoint);
	splineRotation = spline.GetOrientationOnSpline(splinePoint);
}
void HandcarRotation()
{	
	float rotationX = myTransform.rotation.eulerAngles.x;
	float rotationY = splineRotation.eulerAngles.y;
	float rotationZ = myTransform.rotation.eulerAngles.z;
	
	myTransform.rotation = Quaternion.Euler(rotationX, rotationY, rotationZ);
}

void SetToSplinePosition()
{
	//set the local x velocity to zero to help prevent sliding off the spline
	Vector3 localVelocity = myTransform.InverseTransformDirection(myRigidbody.velocity);
	localVelocity = new Vector3(0f, localVelocity.y, localVelocity.z);
	myRigidbody.velocity = myTransform.TransformDirection(localVelocity);
	
	//set handcar position onto spline
	Vector3 position = new Vector3(splinePosition.x, myTransform.position.y, splinePosition.z);
	myTransform.position = position;
	
	//freeze localz rotation
	Vector3 localRotation = myTransform.localRotation.eulerAngles;
	localRotation = new Vector3(localRotation.x, localRotation.y, 0f);
	myTransform.localRotation = Quaternion.Euler(localRotation);
}

void WheelTorque()
{
	frontLeftWheel.motorTorque = speed;
	frontRightWheel.motorTorque = speed;
	backLeftWheel.motorTorque = speed;
	backRightWheel.motorTorque = speed;
}

Tricky…you could try running it under physics most of the time, but disable the rigidbody when you leave the ground. While disabled you could reposition it using your own simplified physics approximation, and re-enable normal physics when you land. Make sure you update the rigidbody velocity manually while it’s disabled, so that it’s ready to resume at any point.

Not certain how well that will work, and it certainly isn’t efficient. But it would give you more precise control than poking at it with forces…

Hi,
Dan Wolley,

I would like to have these functions too …

float splinePoint = spline.GetClosestPoint(myTransform.position, splineAccuracy);
splinePosition = spline.GetPositionOnSpline(splinePoint);
splineRotation = spline.GetOrientationOnSpline(splinePoint);

Please post more details about these functions as I want to use similar for my project.
Thanks,
MSL.