Bezier Curve Tween Isn't working

I’m currently working on a jojo game, and for the barrages I want to use a bezier curve to create something similar to YBA.

I tried following a few tutorials but they didn’t work, so I mixed in stuff from the forums and came up with this

local Debris = game:GetService("Debris")
local HumanoidRootPart = script.Parent.HumanoidRootPart

function lerp(a, b, t)
	return a + (b - a) * t
end

function quadraticBezier(t, p0, p1, p2)
	local l1 = lerp(p0, p1, t)
	local l2 = lerp(p1, p2, t)
	local quad = lerp(l1, l2, t)
	return quad
end

while wait(1) do
	
	local p0 = Instance.new("Part")
	p0.Parent = script.Parent
	p0.Size = Vector3.new(0.1,0.1,0.1)
	p0.Anchored = true
	p0.Position = HumanoidRootPart.CFrame * CFrame.new(0,0,3).Position
	Debris:AddItem(p0,1)

	local p1 = Instance.new("Part") 
	p1.Parent = script.Parent
	p1.Size = Vector3.new(0.1,0.1,0.1)
	p1.Anchored = true
	p1.Position = HumanoidRootPart.CFrame * CFrame.new(-5,0,-3.5).Position
	Debris:AddItem(p1,1)

	local p2 = Instance.new("Part")
	p2.Parent = script.Parent
	p2.Size = Vector3.new(0.1,0.1,0.1)
	p2.Anchored = true
	p2.Position = HumanoidRootPart.CFrame * CFrame.new(0,0,-7).Position
	Debris:AddItem(p2,1)
	
	local ArmModel = script.Parent["Left Arm"]:Clone()
	ArmModel.Anchored = true
	ArmModel.CanCollide = false
	ArmModel.Parent = workspace
	Debris:AddItem(ArmModel,1)
	
	for t = 0,1,1.5 do
		local TargetPosition = quadraticBezier(t , p0.Position, p1.Position, p2.Position);
		local Goal = {
			Position = TargetPosition;
		};
		local Tween = game:GetService("TweenService"):Create(ArmModel, TweenInfo.new(1), Goal);
		Tween:Play();
	end
end

but the part simply goes from the arm to the starting position. I’m completely unfamiliar with bezier so go easy on me with the answers.

This is what i have: https://gyazo.com/a3110b7548cc3df785aa4a4b809018d7

3 Likes

I have a topic on this!

You don’t need to have your own lerp function because ROBLOX already has a :Lerp() method for datatypes like Vector3 and CFrame. You also shouldn’t use TweenService, as it will look wonky. The increment variable in your for loop is also 1.5, that means it only does the loop once (as it only takes one 1.5 to reach 1)

for i = 0, 1, 0.03 do
	local interpolation_1 = p0.CFrame:Lerp(p1.CFrame, i)
	local interpolation_2 = p1.CFrame:Lerp(p2.CFrame, i)
	local interpolation_3 = interpolation_1:Lerp(interpolation_2, i)
	ArmModel.CFrame = interpolation_3
	wait(0.01)
end

Most of the explanation is in my topic so I’d recommend checking it out. Now, this will not make your arm model LookVector (where it is pointed) change, but if you want that behavior and need help with it just ask.

1 Like

This fixed the movement of the arm entirely, but I have no idea how to set the LookVector of the arm, so any help would be appreciated.

To solve this problem, we will use CFrame.lookAt(pos1, pos2). It is a CFrame method that returns a CFrame positioned at pos1, while looking at pos2. Now, we can already deduce that we’re gonna need access to one interpolation ahead, and it wouldn’t be efficient if we calculated one interpolation ahead for every interpolation. So first, we must calculate all the interpolations so they’re already there for use, and store them in a table:

local interpolations = {}
for i = 0, 1, 0.03 do
	local interpolation_1 = p0.CFrame:Lerp(p1.CFrame, i)
	local interpolation_2 = p1.CFrame:Lerp(p2.CFrame, i)
	local interpolation_3 = interpolation_1:Lerp(interpolation_2, i)
	interpolations[#interpolations+1] = interpolation_3
	ArmModel.CFrame = interpolation_3
end

Notice we removed the wait(). That is because we want the calculations to be done as quickly as possible. Now we will sort through the interpolations table using ipairs. Why ipairs? Because it retains the order of the elements in the table, going from first to last. If we used pairs, we run the risk of it being unordered, which is something we don’t want for a system like this.

for index, interpolation in ipairs(interpolations) do
	local nextInterpolation = interpolations[index+1]
	if nextInterpolation then
		ArmModel.CFrame = CFrame.lookAt(interpolation.Position, nextInterpolation.Position)
	else
		ArmModel.CFrame = interpolation
	end
	task.wait(0.01)
end

Doing interpolations[index+1] gives us the interpolation one index ahead. However, this will eventually be nil as we will be at the end of the table. When we reach the end of the table, only the CFrame of the arm model is readjusted.

Also notice that we are indexing the interpolation’s position. That’s because the lookAt method utilizes Vector3s, and our interpolations are CFrames. However, CFrames have a Position property that is a Vector3 of where they are located.

Please post a gif of the finished product if it works and if you’re comfortable with it.

1 Like

Thanks for all the help, now the code all works, and i understand it all too.

Heres the result: https://gyazo.com/4054d3605b0e3dac1f60689e8b0c9cc9

1 Like

That is very good to hear. It looks good as well. If you want it to go faster/slower just tweak with the value inside the task.wait().

Ah yes I see, thank you for that.

Just for anyone following this who wants to use it, if you make the p1 position’s Y value random to spawn the arms at the top or bottom of that side.

p1.Position = HumanoidRootPart.CFrame * CFrame.new(-5,math.random(-4,4),-3.5).Position