Finding rotation of a point on Bezier Curve

Hey there Devs.
I’m working on some concepts for a train system i want to script in the future. I want the trains to run using CFrame so they are smooth and will never de-rail. I plan to do this with Bezier Curves as it is the only solution i have come across for this as of now.

I have found a couple of useful Bezier Curve modules that work perfectly. The only problem is, when interpolating, they do not rotate the part moving along the curve accordingly.

I have tried changing the rotation of the part using CFrame.Angles but i haven’t had any luck getting it to work. I’m not too sure how i go about finding the angle of a point on the curve.

Here is the formula that the script uses to interpolate, where t = percentage of the curve that the current point is at:

CFrame.new(Vector2.new((1-t)^3 * P0 + 3 * (1-t)^2 * t * P1 + 3 * (1-t) * t^2 * P2 + t^3 * P3))`
2 Likes

Well, the first 2 obvious issues is that you are feeding a Vector2 to a CFrame (Vector3 required) and that that Vector2 only has 1 argument, meaning Y = 0. Essentially, you get a CFrame without a rotation offset by whatever that value is on the X axis.

What I would do is take 2 points that are exactly the same distance behind and in front of the current point and feed them to CFrame.new, giving you the required rotation. Then remove the positional offset and add the current point as an offset. This is the expected result:

local point, pointhind,pointfront

local rotcf = CFrame.new(pointhind, pointfront); rotcf = rotcf-rotcf.Position
local finalcf = rotcf+point

Also, that bezier curve is one dimensional (or 2, idk what I am supposed to be seeing here), so you will have to transform it into 3 dimensions.

I’ll skip the maths and give the tl;dr version. To make your part rotate to follow the line, just use this:

CF = CFrame.new(Interpolate(t), Interpolate(t + 0.01))

Where ‘Interpolate’ is the interpolation function of the bezier module you give above, and again, t is the percentage of the curve your current point is at. This should work for most use cases, however the part won’t bank (i.e. it will rotate up and down and left to right, but wont tilt side to side) and it will behave a little weird if you try to make loops.

There’s nothing special about 0.01, it’s just an arbitrary small number so that your part points in the direction of the line.

2 Likes

Ok, thank you for the replies. I didn’t realise it was using a Vector2 because it was still working anyway and interpolating on the X and Y. I will try your solutions when I get home.

Also, another question. The point doesn’t move along the line at the same speed on a Bezier Curve. Some points it goes slower. With my train, I need it to go the same speed at all points on the line. Is this possible?

Oh yeah, it will definitely need a Vector3 to work with the method I posted. It’s not a problem I’ve ever tried to solve before, and doing some quick searching shows there’s not an easy answer. There are approximations but they require a fair bit of maths I’m guessing you’d rather avoid (ios - How to achieve uniform speed of movement on a bezier curve? - Game Development Stack Exchange)

A slightly inefficient but much simpler way to just write some code that repeatedly increases t by small amounts until the point has moved appropriately far away to achieve uniform speed.

Ok thanks. Now that I think about it, I believe the module I am using had a function to get the approximate length of a curve. I could possibly then divide the speed it should go by the length of the curve to get the value of t to go along the line that many studs. I’m not sure if this would work.

1 Like

You essentially need to calculate the tangent vector to that point on the Bezier curve. To do this, you essentially need something called a derivative which is basically just the slope at any point.

The code you gave is basically the parametric equation for a Bezier curve, a1*t^3 + a2*t^2 + a3*t^1 + a4 for each axis. Written in matrix form, you can represent the parametric equation as 2 matrices, A and T; with x(t) = A*T being the very compact form! Matrix T is simply just [t^3; t^2; t; 1]

Then, the first derivative is simply dx/dt = A*[3t^2; 2t; 1; 0]. Use this to calculate the tangent vector, which is essentially the direction that your part will need to face to be perpendicular to the line.

This method is the most accurate way of finding the ‘rotation’ of parts along a Bezier curve. Furthermore it is probably the most computationally efficient way. It is also (in my opinion) the most simple method as you just change the equation very slightly.

1 Like

The method @madattak offered for my initial question works.

My problem now is that, when the Interpolation finishes, the rotation of the part is not exactly at that of the last point.

image

The green block is the final point in the curve, and the gray part is the part that gets moved along the curve.

Any ideas?
Thanks for the help.

Just to clarify, is it only at the very end of the curve this problem arises? The method I wrote makes your part face towards the point 0.01 further down the line. Since the line ends at 1, if t is more than 0.99 then you may get weird results as its making it face towards a point past the actual end of the line. Try making 0.01 smaller to say 0.001, and let me know if this helps at all.

This is because at the end, you calculate the next point on the Bezeir curve which can make some weird results. Use my derivative method in order to calculate the exact tangent line at that point instead of dirty-differentiation.