Humanoid.MoveToFinished:Wait() Doesn't Fire Properly

I’m working on an NPC using Roblox’s Navigation system, but the code is getting stuck while navigation. Specifically, the code is getting stuck on the Humanoid.MoveToFinished:Wait() event. The NPC stops moving and arrives at its waypoint, but it fails to fire the event and the code hangs until the max time expires and it moves forward one waypoint. I suspect that either the NPC is properly arriving at its waypoint and is stopping, but not firing the .MoveToFinished:Wait() event, or there is some kind of memory leak.

Here’s a video of the issue:

In the video, the NPC functions–normalish–until the player moves around the corner. At that time, it switches from raycasting and :MoveTo() to PathfindingService. While not visible in the video, the NPC had already used PathfindingService with no issue to get to the player, but when used again, it started to bug out.

Here’s the code that’s malfunctioning.

local function pathToTarget(char)
	local position = monster.Position
	local player = PlayerService:GetPlayerFromCharacter(char)
	local targetPosition = char:WaitForChild("HumanoidRootPart").Position or char:WaitForChild("Torso").Position
	path:ComputeAsync(position, targetPosition) --Create path
	local waypoints = path:GetWaypoints()
	for i, waypoint in pairs(waypoints) do --This loop makes the monster follow the waypoints to the player it is chasing.
		print("Running Waypoints")
		humanoid:MoveTo(waypoint.Position)	
		
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		print("Waiting")
		humanoid.MoveToFinished:Wait() --This code is getting stuck here. The NPC stops moving, but the event isn't fired.
		print("Done")
	end
	return
end

I’ve tried looking online and I found a few related issues, but none had an answer and many were quite old. Additionally, the Developer Hub didn’t seem to have any useful information. Does anyone know why this is happening?

Edit: I managed to get it working, if you’re looking for a more detailed solution (with code) check post #8.

5 Likes

I had an issue similar to this when I made a humanoid moveto system for a car dealership thing I was working on. What I was able to do to actually solve this was embed a connection by doing something similar to this.

wConnection = humanoid.MoveToFinished:connect(function()
	didReach = true
	wConnection:Disconnect()
	wConnection = nil			
end)

-- freeze the movement of the player so it acts like an animation so to speak, and than unfreeze once the player actually leaves the place

humanoid:MoveTo(position)


-- create a new thread to check if the humanoid reaches and potentially check if they just straightup leave so their is no hanging connection

spawn(function()
	while not didReach do
		if not (humanoid and humanoid.Parent) then
			break
		end
		
		print("still going")
		
		wait(5)
		
		humanoid:MoveTo(position)
	end
	
	if wConnection then
		wConnection:Disconnect()
		wConnection = nil
	end
end)

This method kind of piggybacks off of what the documentation for moveto reccomends you do and i found it worked well.

I’m not sure if I’ve implemented this wrong, but this seems to break the entire navigation system. Instead of the NPC switching smoothly between AI navigations and simple Humanoid:MoveTo(player), the NPC no longer uses AI navigation, although it is running the code, and it is much buggier than before. The old code functions better, but it freezes at the Humanoid.MoveToFinished:Wait() line, making it unusable as well.

local function pathToTarget(char)
	local position = monster.Position
	local player = PlayerService:GetPlayerFromCharacter(char)
	local targetPosition = char:WaitForChild("HumanoidRootPart").Position or char:WaitForChild("Torso").Position
	path:ComputeAsync(position, targetPosition) --Create path
	local waypoints = path:GetWaypoints()
	for i, waypoint in pairs(waypoints) do --This loop makes the monster follow the waypoints to the player it is chasing.
		print("Running Waypoints")
		humanoid:MoveTo(waypoint.Position) --Moves NPC
		
		if waypoint.Action == Enum.PathWaypointAction.Jump then --If jumping is required, make the NPC jump.
			humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		wConnection = humanoid.MoveToFinished:connect(function() --Honestly, I don't even know what this does.
			print("Does something")
			didReach = true
			wConnection:Disconnect()
			wConnection = nil			
		end)
		
		spawn(function() --This spawns a corountine which "might" fix the error where humanoid.MoveToFinished:Wait() fails.
			print("Function spawned")
			while not didReach do
				if not (humanoid and humanoid.Parent) then --I don't even know what this is supposed to do.
					break
				end

				print("still going")

				wait(5)
				print("Trying again to move to position")
				humanoid:MoveTo(waypoint.Position) --Try again to move to position
			end

			if wConnection then
				print("Breaking Connection")
				wConnection:Disconnect()
				wConnection = nil
			end
			
		end)
		print("Done")
		--humanoid.MoveToFinished:Wait()
	end
	return
end

The NPC also appears to “stutter” or freeze at times, like it is lagging or something. Additionally, the code occasionally throws the error Workspace.NPC.Navigate:29: attempt to index nil with 'Disconnect'. Which is coming from this block of code:

wConnection = humanoid.MoveToFinished:connect(function() --Honestly, I don't even know what this does.
	print("Does something")
	didReach = true
	wConnection:Disconnect() --This is throwing an error.
	wConnection = nil			
end)

The game will also hang under certain circumstances, and the NPC’s navigation fails. You can see that in this video:

If you need to see the code in context, I’ve included this script below.

--Variables
local PlayerService = game:GetService("Players")
local pathFindingService = game:GetService("PathfindingService")
local humanoid = script.Parent.Humanoid
local monster = script.Parent:FindFirstChild("HumanoidRootPart") or script.Parent:FindFirstChild("Torso")
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")
local PlayerTag = "PLAYER"
local path = pathFindingService:CreatePath()
local didReach = false

--Functions
local function pathToTarget(char)
	local position = monster.Position
	local player = PlayerService:GetPlayerFromCharacter(char)
	local targetPosition = char:WaitForChild("HumanoidRootPart").Position or char:WaitForChild("Torso").Position
	path:ComputeAsync(position, targetPosition) --Create path
	local waypoints = path:GetWaypoints()
	for i, waypoint in pairs(waypoints) do --This loop makes the monster follow the waypoints to the player it is chasing.
		print("Running Waypoints")
		humanoid:MoveTo(waypoint.Position) --Moves NPC
		
		if waypoint.Action == Enum.PathWaypointAction.Jump then --If jumping is required, make the NPC jump.
			humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		wConnection = humanoid.MoveToFinished:connect(function() --Honestly, I don't even know what this does.
			print("Does something")
			didReach = true
			wConnection:Disconnect()
			wConnection = nil			
		end)
		
		spawn(function() --This spawns a corountine which "might" fix the error where humanoid.MoveToFinished:Wait() fails.
			print("Function spawned")
			while not didReach do
				if not (humanoid and humanoid.Parent) then --I don't even know what this is supposed to do.
					break
				end

				print("still going")

				wait(5)
				print("Trying again to move to position")
				humanoid:MoveTo(waypoint.Position) --Try again to move to position
			end

			if wConnection then
				print("Breaking Connection")
				wConnection:Disconnect()
				wConnection = nil
			end
			
		end)
		print("Done")
		--humanoid.MoveToFinished:Wait()
	end
	return
end

local function raycastchase(target)
	local ignorelist = {monster}
	if target == nil then --If called without a target, return and try again.
		return
	end
	local currentTarget = target:FindFirstChild("HumanoidRootPart") or target:FindFirstChild("Torso")
	local v2root = currentTarget.Position - monster.Position
	local ray = Ray.new(monster.Position, v2root.Unit * 800)
	local hit, position = workspace:FindPartOnRayWithIgnoreList(ray, ignorelist)
	if hit then 
		if hit:IsDescendantOf(currentTarget.Parent) then --If the player is within sight, chase them directly.
			humanoid:MoveTo(currentTarget.Position)
		else -- If the player is not within signt, create a path and follow them.
			pathToTarget(target)
		end
	end
	return
end

--Looping Code (start here)
while true do
	RunService.Heartbeat:Wait() --Ensures code is not run too frequently, but it should be updated as quickly as possible.
	local min = math.huge
	local char = nil

	for i, character in pairs(CollectionService:GetTagged(PlayerTag)) do --Gets all living/targetable players in-game
		local d = (character.PrimaryPart.Position - monster.Position).Magnitude --Determines the distance to each player

		if d < min then
			min = d
			char = character
		end
	end
	raycastchase(char) --Sends the closest player to the rest of the code.
end

Sorry about the huge chunk of code and stuff above, but I’m still not quite sure what exactly is malfunctioning, so I thought it best to include as much as possible.

1 Like

I too am experiencing this exact same issue. Were you able to find a solution?

I’m afraid not, I’m still unable to get this to work. Fortunately, I found that using raycasts, moving the NPC to the player when it can see it (or the players’ last known position if it can’t) works very well. However, if you still want it to navigate obstacles and navigate to a player that’s far away, I don’t have any solutions. If you find one though, please let me know.

Did you set all NPC’s parts network ownership to nil? This could solve the stuttering / freezing

7 Likes

Instead of connecting this to a function and disconnecting you can just do this.

humanoid.MoveToFinished:Wait()

@mattozzo is right, you have to set its ownership to nil, which automatically assigns to server.

1 Like

I have never actually heard of network ownership before. I looked it up and implemented the code necessary to change it’s network ownership. This seems to have fixed the issue for the time being. I haven’t put it through thorough testing yet, but this seems to have fixed everything. Thank you!

For anyone who discovers this thread in the future looking for a solution, here’s the code I added at the beginning of my script.

for i, part in pairs(script.Parent:GetChildren()) do --Gets all children of the NPC
	if part:IsA("BasePart") then --Checks that the child is a basepart.
		part:SetNetworkOwner(nil) --Tells server to handle all physics.
	end
end
6 Likes

Changing the network ownership seems to have fixed the issue I had with humanoid.MoveToFinished:Wait() for the time being. I still need to run some more tests, but hopefully, this solution will be permanent.

1 Like

EDIT: i just realized this post is 2 years old lol. my bad, idk how i didnt notice honestly
still, the bug that i mention in this post is a CURRENT issue, whoever is seeing this PLEASE post a bug report. this is ruining all of my NPCs.

i know that this is, ive been dealing with this bug all week and it is not the fault of your code. you see the way the NPC just randomly jittered in place? if a humanoid is almost at the point its moving to, and that point is above or below the HumanoidRootPart, then the humanoid will ocasionally just jitter out until the MoveTo() times out at ~8 seconds. because the remaining distance is almost perfectly vertical, it is trying to walk vertically. this is a problem introduced by recent updates, before this Roblox NPCs would not attempt to do this. its a rare situation, and you might think you have fixed it. but i can assure you that it is NOT fixed by setting network ownership. setting network ownership to server (nil) is not permanent, and it will often just reset back on the next physics frame, so it is doing practically nothing.

PLEASE, PLEASE, PLEASE SOMEONE MAKE A BUG REPORT FOR THIS (i can’t, as i have no posting permissions and no clue how this bug has gone so unnoticed)

another video showcasing the issue:

(note: you can see MoveTo() isnt being spammed because i put a print after every call to it, and it would be flooding the console way more)

NPC model used (code included): https://create.roblox.com/marketplace/asset/12372211003/Wandering-Guy

if you make sure that the Y position of the MoveTo() target position is always the same as the RootPart’s Y position, the error is fixed.
my temporary workaround (use this instead of combined MoveTo() and MoveToFinished:Wait()):

function MoveToFixed(Humanoid, TargetPosition, Timeout, CancelEvent)
	local Done = Instance.new("BindableEvent")
	local SteppedConnection = nil
	
	local HRP = Humanoid.RootPart
	local LastY = (HRP ~= nil) and HRP.Position.Y or math.huge
	
	local MoveToYAdjusted = (HRP ~= nil) and Vector3.new(TargetPosition.X, HRP.Position.Y, TargetPosition.Z) or Vector3.new(0,0,0)
	Humanoid:MoveTo(MoveToYAdjusted)
	
	SteppedConnection = RunService.Stepped:Connect(function()
		if HRP then
			if HRP.Position.Y ~= LastY then
				LastY = HRP.Position.Y

				MoveToYAdjusted = (HRP ~= nil) and Vector3.new(TargetPosition.X, HRP.Position.Y, TargetPosition.Z) or Vector3.new(0,0,0)
				Humanoid:MoveTo(MoveToYAdjusted)
			end
		else
			Done:Fire()
		end
	end)
	
	
	Humanoid.MoveToFinished:Connect(function()
		Done:Fire()
	end)

	task.delay(Timeout, function()
		Done:Fire()
	end)
	
	if CancelEvent then
		CancelEvent.Event:Connect(function()
			Done:Fire()
		end)
	end

	Done.Event:Wait()
	
	if SteppedConnection then
		SteppedConnection:Disconnect()
	end
end
1 Like