Cubic Bezier Curve

Assuming that Green is the origin and red is the player’s HumanoidRootPart I want it to move like this. I already know how to create the curve and make it move with the curve but since things like this aren’t really my strong spot I’m not sure how to position the second and third points relative to the origin and end to do so.

local folder = workspace:WaitForChild("Folder")

wait(3)

local originPos = folder.PartToMove.CFrame

local pos2 = origin + --offset here for second part

local pos3 = endPart + --offset here for third part

local endPos = workspace.azlentic.HumanoidRootPart.CFrame
2 Likes

Please elaborate on what you want it to appear like. Do you want it to follow the path of those preset positions, do you want it randomized, or offset the same always? What exactly is this for? Please consider asking yourself this before we can give a truly helpful response.

1 Like

I’m using this equation from the developer website and just using a for loop to move it for now.

https://developer.roblox.com/en-us/articles/Bezier-curves

function cubicBezier(t, p0, p1, p2, p3)
	return (1 - t)^3*p0 + 3*(1 - t)^2*t*p1 + 3*(1 - t)*t^2*p2 + t^3*p3
end

for t = 0,1,0.01 do
	local TargetPosition = cubicBezier(t, origin.Position, pos2.Position, pos3.Position, endPos.Position)
	folder.oop.CFrame = CFrame.new(TargetPosition)
	wait()
end

I want to offset it as it appears in the image based on where the origin and end point are positioned.

Very interesting question, any reason why you are using a cubic bezier curve other than a quadratic bezier curve? Anyways here is one way you could do it which involves finding the midpoint.

I’m assuming those two parts are parallel to the direction vector, Otherwise, you can just freely choose your variables like having the offsets not parallel to a direction vector and instead what you can freely substitute in.

local function bezierCurvePoints(position,lookAt,rightLength,length1,length2)
	local upVector = Vector3.new(0,1,0)
	
	local positionToLookAt = lookAt-position
	local unitDirection = positionToLookAt.Unit
	
	--find the right vector relative to the world
	local rightVector = positionToLookAt:Cross(upVector)
	
	local midpoint = position+positionToLookAt/2
	
	--right length influences the length it goes "right"
	local midpointToTheRight = midpoint + rightVector*rightLength
	
	local p2 = midpointToTheRight - unitDirection*length1
	
	local p3 = midpointToTheRight + unitDirection*length2
	
	return p2,p3
	
end

Perhaps this is what you are looking for, although you can simplify it using a quadratic bezier curve since both points p2 and p3 are on the same side if you wish. Although this one may be more adjustable.

Edit: Also this solution assumes the parts p2 and p3 are to the “right” of the direction relative to the world y axis similar to how CFrame.lookAt() finds it’s right vector.

	local upVector = Vector3.new(0,1,0)
	local positionToLookAt = lookAt-position
	--find the right vector relative to the world
	local rightVector = positionToLookAt:Cross(upVector)

This was again based on the image and something you should further consider later if you have an issue with the direction not going right? perhaps if you consider orientation.

1 Like

Well first to solve this you will want to know what offset is desired. This can be accomplished through testing random numbers (time-waster), or typing in a script in the command bar.

For Pos2:

local a = origin
local b = pos2

local offset = a.CFrame * b.CFrame:inverse()

print(offset)

For Pos3:

local a = origin
local b = pos3

local offset = a.CFrame * b.CFrame:inverse()

print(offset)

By pasting these separately in the command bar, it will print the CFrame offset between each of the parts.

With this new offset, you can set Pos2 and Pos3 to origin.CFrame * offset respectively, where offset would be something like CFrame.new(Numbers printed from the command).

I think this would help, but that other guy put some pretty in depth stuff that I haven’t yet read. So uh if this is what you’re looking for then yee haw! Good luck.

1 Like

Thank you! I’ll try that. I’m using a cubic one so that it will move differently depending on how hard the boss is to defeat just to switch it up between them somewhat.

I know what the rightLength, length1, and length2 parameters would be based on the screenshot you sent above but what would I pass for position and lookAt?

Whoops forgot to include those variables, Position and lookAt should correlate the position vectors relative to the world, which in the image is green at the origin (0,0,0) and the lookAt should be the enemy HumanoidRootPart.Position.

1 Like


From the look of it length1 and length2 is just the magnitude between Part2 and midpoint and Part3 and midpoint, unless I misunderstood. If I don’t have the position of Part2 and Part3 how can I calculate length1 and length2?

Nice catch, haven’t considered it while typing the solution. For this issue instead of using a predetermined length magnitude, we can use a percentage of the length of the direction vector if you want the curve to scale in size relative to the length of the direction from the position to lookAt. This percentage, percent1,percent2 will be a decimal percentage that’s pretty arbritrary.

--percent1,percent2 = 0.25, 0.25
--1/4 of the length of the positionToLookAt vector
local function bezierCurvePoints(position,lookAt,rightLength,percent1,percent2)
	local upVector = Vector3.new(0,1,0)
	
	local positionToLookAt = lookAt-position
	local unitDirection = positionToLookAt.Unit
	
	--find the right vector relative to the world
	local rightVector = positionToLookAt:Cross(upVector)
	
	local midpoint = position+positionToLookAt/2
	
	--right length influences the length it goes "right"
	local midpointToTheRight = midpoint + rightVector*rightLength
	
	local p2 = midpointToTheRight - positionToLookAt*percent1
	
	local p3 = midpointToTheRight + positionToLookAt*percent2
	
	return p2,p3
	
end
1 Like

Got it, thanks. In the end it should look like this, right?

local positionToLookAt = endPos.Position-origin.Position
local midpoint = origin.Position+positionToLookAt/2

local rightLength = (.25 / midpoint).Magnitude

local position2, position3 = bezierCurvePoints(origin.Position, endPos.Position, rightLength, 0.25, 0.25)

(Pretty sure I might have messed up the expression calculating rightLength)

Yeah, that right length is odd, maybe you should try out scalar multiplication instead of dividing a number by a vector which I have never seen before.

--length is 1/4 of the length of positionToLookAtVector
--or 1/2 of the length of the midpoint of the positionToLookAtVector
local rightLength = (0.5*midpoint).Magnitude

I have no idea how the luau does it but this weird thing happens if the vector contains a 0 I guess. I think lua tries to perform scalar multiplication with division…it won’t go well especially if the number in the vector is 0 through the below code:

local newUp = Vector3.new(0,0,1)--random vector
local rightLength = (.25 / newUp).Magnitude
print(rightLength)--you will get inf
1 Like

Weird. It actually looks like now that I changed it, it’s positioning both of them inside the origin. Before when I was doing division it completely ignored the curving part of it and went linearly (not sure if that’s actually a word or not but we’ll go with it) instead of curving.

With Division:


With Multiplication:

Hmm, Maybe the cause is something else, I think it’s because I forgot to unitize the right vector so try this.

	--find the right vector relative to the world
--unitize it now
	local rightVector = positionToLookAt:Cross(upVector).Unit
1 Like

Looks like that fixed it. I’m assuming to edit the offset I can just change the upVector right?

Yeah feel free to change the offsets, add a random offset, play around with it. For the direction of the “RightVector” it’ll depend on the arbritrary upVector we set according to the right hand rule:

In this case b = upvector, a = direction vector, and a x b = right vector. As long as you can visuallize what happens when you add, multiply, cross or even d a vector you should be able to make the curve go any way you like.

1 Like

Quick question, what technique are you gonna use to replace the for loops to increment the t ? If it’s run service how are you gonna replicate it to the server?