Bezier Curves Issue

local function QuadraticBezier(t,p0,p1,p2)
	return (1-t)^2*p0+2*(1-t)*t*p1+t^2*p2; 
end;

local Part0 = workspace.Start;--<Start point
local Part1 = workspace.Bend;--<'Curve' Point
local Part2 = workspace.End;--<End Point


for t = 0,1,0.01 do
	local TargetPosition = QuadraticBezier(t , Part0.Position, Part1.Position, Part2.Position);
	local inter_PART = Instance.new("Part", workspace);--<Part To Move
	inter_PART.Position = TargetPosition;
	inter_PART.Anchored = true
end;

I used this code to duplicate these parts in the form of a curve.
The issue:
If you look at this picture it shows them positioned in the curve, but their orientation is not facing the next part. How can I fix this?

2 Likes

I had a similar issue whilst creating a pathfinding script and solved it using this:

-- where:
-- p2 = the part created
-- last2 = the last part that was created (if it is nil then make the part transparent)
-- b = bezier curve module i did not make
-- :GetPoint(i) = gets a point on a bezier in a percentage
p2.Size = Vector3.new(2,2,(last2.Position - b:GetPoint(i)).Magnitude*1.5)
p2.CFrame = CFrame.new(b:GetPoint(i), last2.Position)

Let me know how it goes for you

Im not too familiar with bezier curves/ any CFrame stuff so I am not sure where to put this in my script.

Instead of setting position create a variable where you store the last position.

Then have the part look at it.

Part.CFrame = CFrame.lookAt(TargetPosition, PreviousTargetPosition)

Either that or change them to balls.

Note:edge cases might not appear correctly and will need some extra code to get it.

local function QuadraticBezier(t,p0,p1,p2)
	return (1-t)^2*p0+2*(1-t)*t*p1+t^2*p2; 
end;

local Part0 = workspace.Start;--<Start point
local Part1 = workspace.Bend;--<'Curve' Point
local Part2 = workspace.End;--<End Point


for t = 0,1,0.01 do
	local TargetPosition = QuadraticBezier(t , Part0.Position, Part1.Position, Part2.Position);
	local inter_PART = Instance.new("Part", workspace);--<Part To Move
	inter_PART.Name = t
	local PreviousTargetPosition = QuadraticBezier(t - 0.01 , Part0.Position, Part1.Position, Part2.Position)
	inter_PART.CFrame = CFrame.lookAt(TargetPosition, PreviousTargetPosition)
	inter_PART.Anchored = true
end;

THANK YOU!!

IT nearly worked, but Im not sure why these gaps occur, any ideas?

These gaps are from the calculations, you either have to increase count of parts, make them bigger, or change the calculations.

2 Likes

My bad, I looked at it wrong. They are actually not gaps, its just a bit misaligned.

How do you reccomend fixing this? (Offsetting it a bit after making it was my first idea but I dont know how much of a good idea that is)

Yeah you will probably have to work with offsetting or sizing.

1 Like
for t = 0,1,0.01 do
	local TargetPosition = QuadraticBezier(t , Part0.Position, Part1.Position, Part2.Position);
	local inter_PART = Instance.new("Part", workspace);--<Part To Move
	inter_PART.Position = TargetPosition;
    inter_PART.CFrame = CFrame.new(inter_PART.Position,workspace.End.Position);
	inter_PART.Anchored = true
end;

Letting the parts face the next part is inaccurate, instead you should calculate the derivative, which is the part’s correct LookVector.

Calculating the position:

--from github.com/bstummer/bezier
local function getPos(t: number, points: {Vector3}): Vector3
	local copy = {unpack(points)}
	local n = #copy
	for j = 1, n - 1 do
		for k = 1, n - j do
			copy[k] = copy[k] * (1 - t) + copy[k + 1] * t
		end
	end
	return copy[1]
end

Calculating the derivative:

--from github.com/bstummer/bezier
local function fac(n: number): number
	local f = 1
	for i = 1, n do
		f *= i
	end
	return f
end

local function getDerivative(t: number, points: {Vector3}): Vector3
	local result = Vector3.zero
	local n = #points - 2
	for i = 0, n do
		result += (fac(n)/(fac(i)*fac(n-i))) * t^i * (1-t)^(n-i)
			* (points[i+2] - points[i+1])
	end
	return result * (n+1)
end

Visualizing the path:

for t = 0, 1, 0.1 do
	local pos = getPos(t, points)
	local der = getDerivative(t, points)
	
	local part = Instance.new("Part")
	part.Anchored = true
	part.CFrame = CFrame.lookAt(pos, der + pos)
	part.Size = Vector3.one
	part.TopSurface = Enum.SurfaceType.Smooth
	part.BottomSurface = Enum.SurfaceType.Smooth
	part.Parent = workspace
end

You can also use CFrame points instead of Vector3. This automatically calculates the orientation.

--from github.com/bstummer/CutsceneService
local function getCF(t: number, points: {CFrame}): CFrame
	local copy = {unpack(points)}
	local n = #copy
	for j = 1, n - 1 do
		for k = 1, n - j do
			copy[k] = copy[k]:Lerp(copy[k + 1], t)
		end
	end
	return copy[1]
end

This is due to the nature of Bézier curves, points are not evenly distributed by default. This can be fixed with arc length parameterization.