Adding Rotational Roll Into Bezier Curves

I spent this afternoon researching Bezier Curves and how to create them. And I’ve successfully been able to create these curves and implement a simple method to introduce yaw & pitch into the Bezier curve. The end goal is to use Bezier Curves to create a curve for a Roller Coaster or Helicopter to follow.

What I’m trying to figure out now is how to identify roll into the curve. What I mean by this is the parts “tilt” into the curve similar to a roller coaster or how an airplane turns.


So far I’ve been searching the DevForum for previous articles. This is how I was able to figure out how to introduce pitch and yaw into my Bezier Curve. This is achieved through using CFrame.new(Position,LookAt). Both Vector3 values are calculated using the Interpolation equation. With LookAt using a slightly higher Alpha value. Understandably this is not perfect but it will be good enough for my use case. But I was unable to find ways to implement roll.

I’ve also tried to read through some external forums (mainly about Unity) but these seem to use functions accustomed to Unity. So I was a bit lost in how to carry this over into Roblox Studio.

Current Progress Code / Photos

image
In this photo I would want the parts to tilt to the left slowly to bank into the turn. This is what I’m expecting to get explenations on for.

This code is functioning, but I will post it so people understand my current work. Most of this code is just here to help me visualize the curve to make sure everything is working correctly.

local function createCubicBezierCurve(pointStart,pointMidNear,pointMidFar,pointEnd)
	local Folder = Instance.new("Folder")
	Folder.Name = "CurveNodes"
	Folder.Parent = workspace
	for alpha = 0,1,0.01 do
		local goal = cubicBezierCurvePoint(pointStart,pointMidNear,pointMidFar,pointEnd,alpha)
		local direction = cubicBezierCurvePoint(pointStart,pointMidNear,pointMidFar,pointEnd,alpha+0.005)
		local Node = Instance.new("Part")
		Node.Anchored = true
		Node.CanCollide = false
		Node.CFrame = CFrame.new(goal,direction)
		Node.Name = "Node @ Alpha: "..alpha
		Node.Size = Vector3.new(4,0.25,2)
		Node.Parent = Folder
	end
end

And this is my mess of a function that gets the points. yet again, nothing broken, just trying to document my progress.

local function cubicBezierCurvePoint(pointStart,pointMidNear,pointMidFar,pointEnd,alpha)
	local Lerp1 = pointStart:Lerp(pointMidNear,alpha)
	local Lerp2 = pointMidNear:Lerp(pointMidFar,alpha)
	local Lerp3 = pointMidFar:Lerp(pointEnd,alpha)
	local Lerp12 = Lerp1:Lerp(Lerp2,alpha)
	local Lerp23 = Lerp2:Lerp(Lerp3,alpha)
	local LerpFinal = Lerp12:Lerp(Lerp23,alpha)
	return LerpFinal
end
1 Like

In order to add a rotational roll I believe you will need more information to obtain the orientation of the parts.

You have already obtained a definite value for the look vector component of the CFrame orientation based on the path of the bezier curve. However as you said the roll remains a mystery. This is because all the remaining vector components left to fill in the CFrame matrix which are the UpVector and RightVector have infinite options along a plane defined by the lookvector. Basically you are trying to find something like the normal of this graph curve which currently is a best guess based on the cross product with the world’s up vector and the definite look vector.

In 2D there is only one option for the normal to the curve which could represent the up vector Roll of the rollercoaster curve. That doesn’t work too well for 3D with the additional axis.

To solve this you can use this current best guess by the up vector and add another variable for the user to manipulate the roll like in rollercoaster tycoon where you can manually control the yaw pitch and roll of the roller coaster. For this I would rotate the current CFrame value using CFrame.Angles(0,0,Z) along the z axis by what the user desires then interpolate this rotation as well. This value Z will have to be manually inputted in so yeah perhaps by the user using their mouse wheel to change the value of Z in order to give them control over the roll of the coaster.

4 Likes

Thank you for your response, I will try to implement this and get back to you with my progress.

Although I’m making progress I’m a bit stuck on what @dthecoolest was explaining. Currently I’m trying to apply the rotational value though the interpolation function except the issue I’m running into is that the function uses a range of 0 to 1 so what ends up happening is the curve rotates roughly 360 degrees. I understand they mentioned using an UpVector of the CFrame but was unsure aw how to implement this.

There is definitely a rotational roll present but I’m missing something to keep the roll from following the path of the curve.

local function createCubicBezierCurve(pointStart,pointMidNear,pointMidFar,pointEnd,rotationValue)
	local Folder = Instance.new("Folder")
	Folder.Name = "CurveNodes"
	Folder.Parent = workspace
	for alpha = 0,1,0.01 do

--These three values are important...

		local goal = cubicBezierCurvePoint(pointStart,pointMidNear,pointMidFar,pointEnd,alpha)
		local direction = cubicBezierCurvePoint(pointStart,pointMidNear,pointMidFar,pointEnd,alpha+0.005)
		local rotation = cubicBezierCurvePoint(pointStart,pointMidNear,pointMidFar,pointEnd,alpha)



		local Node = Instance.new("Part")
		Node.Anchored = true
		Node.CanCollide = true

--And changing the CFrame...   (The rest is unimportant for this)

		Node.CFrame = CFrame.new(goal,direction) * CFrame.Angles(0,0,math.rad(rotationValue+rotation.Z)) --rotationValue is a user inputted value that is constant throughout the curve.


		Node.Name = "Node @ Alpha: "..alpha
		Node.Size = Vector3.new(4,0.25,2)
		Node.Parent = Folder
	end
end

image

Notice that the start of the curve has the top of the part facing downwards.

The center of the curve looks good, but the curve doesn’t look correct at the ends.

2 Likes

Nercobumping here but if you are still curious the problem facing you is the Frenet Normal.
It’s this weird math thing that is technically correct but looks ugly.

1 Like