Detecting if ControllerManager has reached certain position (or basically a waypoint)

I’ve been working with ControllerManager too, and I totally get your frustration — it doesn’t have a built-in .MoveToFinished event like Humanoid:MoveTo(), so detecting arrival isn’t as straightforward.

But don’t worry! Here are a few solid strategies that can help make this more reliable:

  1. Use a Region-Based Arrival Check (Better Than Magnitude)

Instead of checking raw .Magnitude every frame (which can be noisy due to slight model jitters), try defining a small radius or region and check if the NPC’s HumanoidRootPart is within that area.

Here’s a simple example:

local ARRIVAL_RADIUS = 2

function hasReachedPosition(npc, targetPosition)
	local root = npc:FindFirstChild("HumanoidRootPart")
	if root then
		local horizontalDist = (Vector3.new(targetPosition.X, 0, targetPosition.Z) - Vector3.new(root.Position.X, 0, root.Position.Z)).Magnitude
		local verticalDist = math.abs(targetPosition.Y - root.Position.Y)
		return horizontalDist <= ARRIVAL_RADIUS and verticalDist <= 3
	end
	return false
end

Call this check in a loop or use RunService.Heartbeat until it’s true.

  1. Use Waypoints from PathfindingService

If you’re using PathfindingService, it gives you waypoints. You can move the NPC toward each one, and after each movement, use the arrival check above.

for i, waypoint in ipairs(path:GetWaypoints()) do
	controllerManager:MoveTo(waypoint.Position)
	
	repeat
		RunService.Heartbeat:Wait()
	until hasReachedPosition(npc, waypoint.Position)
end

This mimics Humanoid:MoveTo() behavior pretty closely and works well with ControllerManager.


  1. ControllerManager Doesn’t Have Events — So You Need Polling

Unfortunately, as of now, ControllerManager doesn’t fire events like MoveToFinished. So yes — polling (looping + checking position) is currently the safest method unless Roblox adds more event-driven APIs in the future.

You can optimize your polling to avoid checking every frame — maybe check every 0.1 seconds using task.wait(0.1) instead of RunService.Heartbeat.

Bonus Tip: Add Debug Visuals!

When debugging NPC movement, it’s super helpful to visualize waypoints and destination zones using Part instances or drawing tools like DrawingService (via plugins).

:white_check_mark: Summary

• Use a radius-based position check (instead of raw magnitude every frame).
• For pathfinding, loop through waypoints and wait until each one is reached.
• Since ControllerManager lacks movement events, polling is necessary — but you can do it smartly.