- Home /
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
Answer by DanMarionette · Jan 17, 2012 at 10:33 AM
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;
}
Answer by Winterblood · Jan 13, 2012 at 11:25 PM
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 for your quick response. This is actually a method I have been trying. It works for most of the time, even at speed. The problems come in when I want the handcar to jump up a ramp or just simple jump in the air via player control. As this is a handcar on rails I need this solution to be rock solid. Another line I've been going down is creating counter force to stop it from sliding left and right (x axis) but this is proving difficult also. I don't know if my maths is wrong or if I'm not applying the forces correctly. It's a shame because constraining the axis in the rigidbody inspector work well but these don't seem to work for local axis.
Oh, it's on rails? I think a counter force matched to the local-X component of the handcar's momentum is the right solution, but I'm not sure how the rotational inertia should be handled, to be honest. Try splitting the x force in half and applying each half to one of the two front wheels to give it some twist?
I'm using wheels because I want a "realistic" motion when the handcar goes up and over a ramp. In fact that is why I'm trying to use physics but I need the forces to also act on the handcar when it's off the ground so it can jump whilst travelling around a corner. I know that's not realistic but, it's what I would like to achieve if possible. The only other solution is to ditch physics all together but I lose the nice motion when jumping and going over ramps.
Answer by Winterblood · Jan 16, 2012 at 10:36 PM
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...
Answer by msl_manni · Dec 17, 2012 at 08:27 AM
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.