Bhristt's Bezier Curve Module (Tween support)

Nice module! Would be cool if you could include degree elevation, subdivision and normal vectors.

De Casteljau’s algorithm is actually much faster than the explicit definition, you can try it yourself:

local points = {Vector3.new(0,1.324,38), Vector3.new(9.28,0,2),
	Vector3.new(0,0.38292,4.372), Vector3.new(0.32762,78.281,9)}

local function deCasteljau(t:number):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

local function fac(n:number):number
	if n == 0 then return 1 end
	return n * fac(n - 1)
end

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

Looks pretty nice for a free module, Is it optimized though?

hello! how will i go on about making the cframe tween of the part also have fromeuruleranglesxyz applied at the same time?

I want to thank you for this Module. There are several out there that performs bezier tweens, but I particularly love this one due to the real time path node change in position affecting the object already in motion. Such a nice feature. Creates some really nice organic-like path movements. Thank you.

Is there any built-in way to convert a position to a point in the curve? “CalculatePositionAt” seems to do the exact opposite, so right now the closest I managed to get was this:

local Start, End, Current = Points[1].Position, Points[#Points].Position, RootPart.Position
local NumCurrent = string.format("%.2f", ((Current - Start).Magnitude / (End - Start).Magnitude))

Which seems to work fine at the moment, but is probably innacurate as it uses the magnitude between the start and the end points of the curve instead of the actual curve’s length or whatever metric the actual module uses (normally I just avoid making a post and just fork whatever feature is missing in, but maths are not my thing).


I imagine this isn’t getting any updates anymore (which is totally alright), but I think it’d be cool if we had more control over the tweening functions such as letting us start from a custom percentage (so if for example in my game I wanna make it so when players touch a point in the curve, the tween starts from said point) and letting us decide at what point of the curve the tween ends (which I got working by inverting the NumberValue so the tween plays in reverse based on what end is closest, but I don’t think this is ideal).

There’s also Catmull-Rom Splines which I imagine would be nice for having more control over the curves, but I can’t really imagine how complicated supporting something like that would really be.


Regardless of the previous, thank you, been using this since july of last year for cutscenes, visual curves and such (currently trying to use it for rail grinding), and it’s extremely useful and way more accessible than any of the alternatives I’ve seen so far, so huge thanks for making such a well made module.

2 Likes

I’ve already made a module for Catmull-Rom Splines, you should check it out!

As for this module, I think I’m going to make a V2 later on. This was a very quickly made version of something I had in mind, but I can definitely make a better functioning one! Thanks for the feedback !

1 Like

oh, I didn’t notice you were the creator of that one too :sweat_smile:, ngl, I tried it yesterday and I couldn’t get it to work, but I just tried porting my system to work with V1 and it works wonderfully!

I’m having an issue though, when transitioning from V1 to V2 I get an error telling me splines can’t have more than 3 points (which I assume requires a path), and when I try to use paths instead I get an error telling me i’m trying to use the Calculate functions from a table.

Do you think you could add the same examples from V1 updated to work with V2? I don’t think I fully understood how to use the paths and splines together.

1 Like

Of course, I’ll do that soon! I’m not sure what the problem could be as of right now, but I will look into that. I think a tutorial for how to use V2 is necessary, I just haven’t gotten to it yet ;-;

For now, if V1 works better for you, I suggest using V1. It should work the same as V2, but a little less efficient and more prone to errors. V2 has multiple modules and is more organized, but I haven’t fully documented. I’ll get to that ASAP!

1 Like

Hey, I have a question, how do I use the Bezier and turn it into a beam? I want to make it so it shows the trajectory of a bullet so players can aim. How to turn the bezier into a beam?

Hey! I love this!!

I have a question about using models as a replacement for lines. While I haven’t yet tried to do this, do you have plans to implement passing a model for the module use to generate as path sections? Each section stretching to meet the next.

I’m trying to create cart ride track sections with nice curves, but I keep running into issues trying to find ways that make the building workflow easier. I’m not a builder, so doing it by hand takes me forever. If you have suggestions for using your module in a way that smoothly places cart track rails, that would be amazing!!

Amazing work regardless, I look forward to using this in my future projects!

hey, question, is there any way to change the bezier interpolation on this, so it can be a long 5+ point bezier curve but only a quadratic

Hey, very nice module, but I have a question. Is there a way to make a tween move at a constant speed? I’d like for my part to always move at the same speed no matter how far the ending point is.

Make sure you’re using Bezier:CalculatePositionRelativeToLength(). This function takes in a percentage of the length of the Bezier curve and returns the position at the given percentage.

Since the object is moving at a constant speed, you can track how much distance the object has covered and use this to get the percentage of the length of the curve.

distance = speed * time
dx = speed*dt

By adding intervals of distance covered every time step, we can accurately keep track of distance covered in real time.

local speed = 10
local position = 0
local bezier = Bezier.new() -- your bezier curve

-- make sure your bezier curve is completely updated and ready to use
-- it should not be updated any further after this point, otherwise
-- the length of the curve should be updated accordingly

-- we are using bezierLength to refer to the length of our curve
local bezierLength = bezier.Length

local lastTimeStamp = os.clock()

while task.wait() do

    local currentTimeStamp = os.clock()
    local dt = currentTimeStamp - lastTimeStamp

    position += speed*dt

    if (position > length) then
        break
    end
    
    -- this gives the current position assuming constant speed
    local currentPosition = bezier:CalculatePositionRelativeToLength(position/length)

    lastTimeStamp = currentTimeStamp
end

Thanks, but that doesn’t seem to be working for me:

local dis = (Char.HumanoidRootPart.Position - mouseP).Magnitude
		
		local speed = 10
		local position = 0
		local NewBezier = Bezier.new((Char.HumanoidRootPart.CFrame * CFrame.new(0,0,-3)).Position, (Char.HumanoidRootPart.CFrame * CFrame.new(0,0,-15)).Position,  (Char.HumanoidRootPart.CFrame * CFrame.new(math.random(dis*-1, dis),math.random(1, 8), -dis/2)).Position, mouseP)


		-- make sure your bezier curve is completely updated and ready to use
		-- it should not be updated any further after this point, otherwise
		-- the length of the curve should be updated accordingly

		-- we are using bezierLength to refer to the length of our curve
		local bezierLength = NewBezier.Length

		local lastTimeStamp = os.clock()
		task.spawn(function()
			while task.wait() do

				local currentTimeStamp = os.clock()
				local dt = currentTimeStamp - lastTimeStamp

				position += speed*dt

				if (position > bezierLength) then
					break
				end

				-- this gives the current position assuming constant speed
				local currentPosition = NewBezier:CalculatePositionRelativeToLength(position/bezierLength)

				lastTimeStamp = currentTimeStamp
			end			
		end)

		

		local TweenInf = TweenInfo.new(2, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
		local Tween2 = NewBezier:CreateCFrameTween(Fireball, {"CFrame"}, TweenInf)
		
		Tween2:Play()

try this:

local dis = (Char.HumanoidRootPart.Position - mouseP).Magnitude

local NewBezier = Bezier.new((Char.HumanoidRootPart.CFrame * CFrame.new(0,0,-3)).Position, (Char.HumanoidRootPart.CFrame * CFrame.new(0,0,-15)).Position,  (Char.HumanoidRootPart.CFrame * CFrame.new(math.random(dis*-1, dis),math.random(1, 8), -dis/2)).Position, mouseP)

local OBJECT_PATH_SPEED = 10 -- set this to the speed you want the object to travel at


local TweenInf = TweenInfo.new(NewBezier.length/OBJECT_PATH_SPEED, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
local Tween2 = NewBezier:CreateCFrameTween(Fireball, {"CFrame"}, TweenInf, true)

Tween2:Play()
1 Like

It works perfectly now, thanks!

could ya answer mine if possible ?

I seem to be having another issue:


So as you can see, projectiles being moved along the curve seem to be bouncing up and down. I should mention that this doesn’t always happen, and it’s weirdly inconsistent. I don’t believe this is being caused by anything in my script, as the only;thing moving the projectile is this module.

Edit: I fixed this by just anchoring the part

That isn’t really how bezier curves work? The number of points determine the degree of the curve

The closest solution would probably be splines (combining two bezier curves at their ends)

i mean as in combining bezier curves lol also i found a solution like the day after saying that