Smooth waypoint corner calculation

Hi. I am currently trying to make pathfinding service generate smooth turns for sharp corners, however I am not smart enough with math to figure out how to do this. I have already incorporated logic to find out whether or not the angle is too sharp, but I cannot for the life of me figure out how to use a Bezier or lerp or whatever to make it work. Here’s an image describing shortly what I want to achieve here?

EDIT: No I do not want to toy with agent radius.

image

I am sure there is a way to do this properly, I just can’t quite put my finger on it. Any help would be appreciated!

	local function CalculateWaypoints()
		
		local Path = PathfindingService:CreatePath(AgentParameters)
		
		Path:ComputeAsync(Alternative.HumanoidRootPart.Position, Target.HumanoidRootPart.Position)

		Path.Blocked:Connect(function(blockedWaypointIndex)
			Path:ComputeAsync(Alternative.HumanoidRootPart.Position, Target.HumanoidRootPart.Position)  -- Recompute the path if blocked
		end)

		local Waypoints = Path:GetWaypoints()
		local AdvancedWaypoints = {}

		for Index = 2, #Waypoints - 1 do
			
			local current = Waypoints[Index]
			local previous = Waypoints[Index - 1]
			local next = Waypoints[Index + 1]

			local currentDirection = (current.Position - previous.Position).unit
			local nextDirection = (next.Position - current.Position).unit

			local averageDirection = (currentDirection + nextDirection).unit
			local cFrame = CFrame.lookAt(current.Position, current.Position + averageDirection)

			AdvancedWaypoints[Index] = {
				["Position"] = current.Position,
				["CFrame"] = cFrame,
				["Corner"] = false
			}

			local dotProduct = currentDirection:Dot(nextDirection)
			local angle = math.deg(math.acos(dotProduct))

			if angle > 45 then
				AdvancedWaypoints[Index]["Corner"] = true
			end
		end

		return AdvancedWaypoints
	end
1 Like

try to expand the range of the angle, it won’t always be 45 degrees.

local trajectory = math.clamp(30,50)

if angle > trajectory then
				AdvancedWaypoints[Index]["Corner"] = true
			end

Im not sure if this will work, but give it a shot.

1 Like

This isn’t what I was talking about but thank you!

I found the solution for anyone who comes here in the future. Basically, you want to re-calculate the path and only select the points which have an angle threshold above 3 degrees or so. Then you want to use the SimpleSpline module:

To calculate a path between those points, and you have a smooth path!

As a result you get paths like this:

Here’s a snipped from my code for anyone who stumbles upon this in the future:

function SeekTarget(Target)
	
	States["Seeking"] = false
	
	local function CalculateWaypoints()
		
		local Path = PathfindingService:CreatePath(AgentParameters)
		
		Path:ComputeAsync(Alternative.HumanoidRootPart.Position, Target.HumanoidRootPart.Position)

		Path.Blocked:Connect(function(blockedWaypointIndex)
			Path:ComputeAsync(Alternative.HumanoidRootPart.Position, Target.HumanoidRootPart.Position)  -- Recompute the path if blocked
		end)

		local Waypoints = Path:GetWaypoints()
		local AdvancedWaypoints = {}

		for Index = 2, #Waypoints - 1 do
			
			local current = Waypoints[Index]
			local previous = Waypoints[Index - 1]
			local next = Waypoints[Index + 1]

			local currentDirection = (current.Position - previous.Position).unit
			local nextDirection = (next.Position - current.Position).unit

			local averageDirection = (currentDirection + nextDirection).unit
			local cFrame = CFrame.lookAt(current.Position, current.Position + averageDirection)

			AdvancedWaypoints[Index] = {
				["Position"] = current.Position,
				["CFrame"] = cFrame,
			}

			local dotProduct = currentDirection:Dot(nextDirection)
			local angle = math.deg(math.acos(dotProduct))

			if angle > 45 then
				AdvancedWaypoints[Index]["Corner"] = true
			end
			
			AdvancedWaypoints[Index]["Angle"] = angle
		end

		return AdvancedWaypoints
	end
	
	local function NavWaypoints(Waypoints)
		
		local function Center(From)
			
			local rayOrigin = From
			
			local NewCFrame = CFrame.new(From,RootPart.CFrame.LookVector)
			
			local rayDirectionLeft = NewCFrame.RightVector * -10
			local rayDirectionRight = NewCFrame.RightVector * 10

			local raycastParams = RaycastParams.new()
			
			raycastParams.FilterDescendantsInstances = {Alternative}
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			raycastParams.IgnoreWater = true

			local rayResultLeft = workspace:Raycast(rayOrigin, rayDirectionLeft, raycastParams)
			local rayResultRight = workspace:Raycast(rayOrigin, rayDirectionRight, raycastParams)

			local leftPoint = rayResultLeft and rayResultLeft.Position or (rayOrigin + rayDirectionLeft)
			local rightPoint = rayResultRight and rayResultRight.Position or (rayOrigin + rayDirectionRight)

			local centerPoint = (leftPoint + rightPoint) * 0.5

			return centerPoint
		end
		
		local function CheckCorner(Current)

			for Index, WaypointData in pairs(Waypoints) do
				if Index > Current and Index - Current <= 5 and WaypointData["Corner"] == true then
					return Index
				end
			end
			
			return false
		end
		
		local Points = {}
		
		for Index,Waypoint in pairs(Waypoints) do
			if ((Waypoint["Angle"] > 3) or Index == 2 or Index == #Waypoints) then
				
				local P = Instance.new("Part")

				P.Size = Vector3.new(.5,.5,.5)
				P.Material = Enum.Material.Neon
				P.CanCollide = false
				P.Parent = workspace
				P.CFrame = Waypoint["CFrame"]
				P.Anchored = true
				
				table.insert(Points,Waypoint.CFrame * CFrame.new(0,0,0))
			end
		end
		
		if #Points ~= 0 then
			
			if Path then
				Path:Stop()
				Path = nil
			end

			Spline = SimpleSpline.new(Points, 0.5, 0)

			Spline:SetOptions({
				Uniform = true,
				Offset = CFrame.new(),
				Loop = false,
				Reverse = false
			})
			
			Spline:Visualize()
			
			local Object = Alternative
			
			local Speed = 16

			Path = Spline:FollowPath(Object, Speed)
		end
	end
	
	if tick() - LastSeek > SeekDebounce then

		LastSeek = tick()
		
		AdvancedWaypoints = CalculateWaypoints()
		
		NavWaypoints(AdvancedWaypoints)
	end
end

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.