How to smooth out abnormal CFrame path

I’ve been working on a script that moves a part from point A to B while avoiding a middle area


The problem is that the change from the normal CFrame to the avoidance CFrame looks too choppy and I would like it to gradually switch CFrames.
Script:

local Colors = script.Parent.Map:WaitForChild('Colors')
local blackHole = Colors:WaitForChild('Red')
local whiteHole = Colors:WaitForChild('Blue')

local ball = workspace:WaitForChild("CFrameBall")
local center = script.Parent.Map.GoodGrave.Position -- Use the position of the black hole as the center of the path
local radiusX = 10 -- Adjust the X radius as needed
local radiusZ = 65 -- Adjust the Z radius as needed
local speed = 0.06 -- Adjust the speed as needed

local furthestX = center.X
local furthestZ = center.Z

-- Function to move the ball in an eyeball-shaped path around the center
local function moveInEyeballPath(ball, center, radiusX, radiusZ, speed)
	local angle = 0

	while true do
		local x = center.X + radiusX * math.cos(angle)
		local z = center.Z + radiusZ * math.sin(angle)
		
		-- Calculate the elliptical avoidance area
		local ellipseRadiusX = 25  -- Adjust the X radius of the ellipse
		local ellipseRadiusZ = 35 -- Adjust the Z radius of the ellipse

		local distanceToCenter = math.sqrt(((x - center.X) / ellipseRadiusX)^2 + ((z - center.Z) / ellipseRadiusZ)^2)

		if distanceToCenter < 1 then 
			-- Use a threshold value (1 for a smooth transition)
			-- Adjust position to avoid the ellipse
			local avoidanceAngle = math.atan2((z - center.Z) / ellipseRadiusZ, (x - center.X) / ellipseRadiusX)
			x = center.X + ellipseRadiusX * math.cos(avoidanceAngle)
			z = center.Z + ellipseRadiusZ * math.sin(avoidanceAngle)
		end

		-- Update furthest point reached
		if x > furthestX or z > furthestZ then
			furthestX = x
			furthestZ = z
		end

		-- Update ball position only if it's at the furthest point reached
		ball.Position = Vector3.new(furthestX, center.Y, furthestZ)

		angle = angle + speed
		if angle >= math.pi * 2 then
			angle = angle - math.pi * 2
		end

		wait(0.01)
	end
end

--
-- Start moving the ball in an eyeball-shaped path
moveInEyeballPath(ball, center, radiusX, radiusZ, speed)

Thank you

I’d recommend a beziercurve.

The problem is you have a parametric function you described with code but you aren’t normalizing this function. If you don’t normalize your parametric functions t-step you get movement which isn’t correlated to the distance. It appears that the only variables you are changing are X and Z, so to fix this what you can do is rewrite your code so you have a function which gives you the x and z variable based on the current time step which appears to be angle in your code. From there what you can do is write code that calculates the length of your path. It seems you have written your code to have an interval between [0, 2pi] and have it reset after. One way to calculate the length of a parametric function is to take the integral between the range and for each parametric value that is changing, create a vector of it and integrate the length of that vector with respect to the t step.

Now you seem to be using 2 functions in your code for your little arc.

Keep in mind, you can simplify your parametric function to just be one which is

x = center.X - radiusX * math.cos(angle)
z = center.Z + radiusZ * math.sin(angle)

and instead of having it go from 0 to 2pi, have it go from 0 to pi. Now since it is uniform since you arnet having the avoidanceAngle’s atan2 function make the function now uniform, it should be a smooth transition now.