# 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
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
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.