Problem with Humanoid:MoveTo()

I’m trying to make an AI for a monster which chase the player.

The problem is that MoveTo() slow down the Humanoid before reaching the waypoint. However, I don’t want the Humanoid to slow down when it’s close of reaching the waypoint. The result is a Monster that make quick breaks between each waypoint.

I looked in roblox docs and others post. I also tried using a “PhysicalWaypoint” which is a part with the same position as the next waypoint, and once it touch the Character of the monster, it goes to the next waypoint. But it reduces the precision of the movement of the monster and is harder to handle.

How can I fix this problem ? Should I overshoot the destination in MoveTo and stop the Humanoid once it reachs the actual destination ? Or is there a better solution

Thank you !

1 Like

Are you running a loop for :MoveTo()?

If you have a task.wait() in the loop it’s probably because it’s yielding until the wait is over.

It would be more helpful if you could post the code

No, I did test and saw that the Humanoid was doing breaks. So I debugged everything and found 2 problem : MoveToFinished and MoveTo() .

MoveToFinished fire only when the Humanoid finish MovingTo so it fire when the Humanoid stop because MoveTo() slow down the Humanoid when it’s close to the goal.

And the monster movement doesn’t look smooth at all.

Can you please post the code so I can figure out what’s going wrong

1 Like
local Character = script.Parent
local Humanoid = Character.Humanoid
local Target = workspace.AITarget
local CharacterScale = Character:GetExtentsSize()
local PhysicalWaypoint = Instance.new("Part", workspace)

--PhysicalWaypoint params :
Side = 2.38

Side = math.sqrt(2 * Side^2)

PhysicalWaypoint.Size = Vector3.new(Side, Side, Side)
PhysicalWaypoint.Shape = Enum.PartType.Cylinder
PhysicalWaypoint.Orientation = Vector3.new(0, 0, 90)
PhysicalWaypoint.CanQuery = false
PhysicalWaypoint.CanCollide = false
PhysicalWaypoint.CanTouch = true
PhysicalWaypoint.Anchored = true
--Services :
local PathFindingService = game:GetService("PathfindingService")

local AgentParams = {
	AgentRadius = CharacterScale.X / 2,
	AgentHeight = CharacterScale.Y,
	AgentCanJump = true,
	AgentCanClimb = false,
	WaypointSpacing = 4,
	Costs = {
		--[Enum.Material.Grass] = 2,  -- Make the agent prefer walking on grass
		--[Enum.Material.Sand] = 10  -- Make the agent avoid walking on sand
	}
}

local Path = PathFindingService:CreatePath()



local function ComputePath(Goal)
	return pcall(function()Path:ComputeAsync(Character.HumanoidRootPart.Position, Target.Position) end)
end


local CharacterTouching = false

local function OnTouched(Touching)
	--print(Touching)
	if Touching:IsDescendantOf(Character) then
		--print("good !")
		CharacterTouching = true
	end
end

PhysicalWaypoint.Touched:Connect(OnTouched)


local DebuggingCoroutines = {}


local function CheckStuck()
	--print("starting coroutine")
	task.wait(5)
	--print("final behavior")
	--for i, child in pairs (DebuggingCoroutines) do print(i, " = ",coroutine.running()) end
	if not DebuggingCoroutines[coroutine.running()] then  return end --print ("coroutine expired : ", coroutine.running())
	if not CharacterTouching then
		--print("Problem detected! Trying to reboot the path by moving PhysicalWaypoint or setting Character touching to true. Or maybe trying to go directly to target.")
		CharacterTouching = true
	end
end


local function Main()
	local Succes, Error = ComputePath(CharacterScale)

	if not Succes then print("ERROR COMPUTING PATH") task.wait(0.5) return end

	local Waypoints = Path:GetWaypoints()

	Humanoid:MoveTo(Waypoints[2].Position)
	if Waypoints[2].Action == Enum.PathWaypointAction.Jump then Humanoid.Jump = true end
	local before = tick()
	PhysicalWaypoint.Position = Waypoints[2].Position
	--print(tick()-before)

	-- Create a coroutine

	
	local checkCoroutine = coroutine.create(CheckStuck) -- Start the coroutine
	DebuggingCoroutines[checkCoroutine] = true
	coroutine.resume(checkCoroutine)

	--PhysicalWaypoint.CFrame = PhysicalWaypoint.CFrame * CFrame.lookAt(PhysicalWaypoint.Position, Waypoints[2].Position)
	--PhysicalWaypoint.Touched:Wait()
	while not CharacterTouching do
		wait()
		--print("not touching")
	end
	--print("we're here")
	DebuggingCoroutines[checkCoroutine] = nil
	
	--print("here problem")
	CharacterTouching = false
end

local IncrementValue = 0.01




while true do
	--print("starting again")
	Main()
end

Here you go, if you have any questions about it, tell me.

It works here, but the Humanoid isn’t precise. Because it change waypoint without totally reaching it. However, in this case the Humanoid doesn’t slow down and keep a speed close to his WalkSpeed

What is your actual goal with the monster? Do you just want it to chase the player or are you trying to get it to move around in a certain area?

I want him to chase the players for now. So I’m doing some test.

Ok, if you just want to make a monster that chases the player, you can just use Humanoid:MoveTo()

local Players = game:GetService("Players")
local Humanoid = script.Parent:FindFirstChild("Humanoid")
local chaseRange = 15 -- range in studs
while true do
	for _, player in pairs(Players:GetPlayers()) do
		if not player.Character then continue end
		local distance = (player.Character.HumanoidRootPart.Position - script.Parent.HumanoidRootPart.Position).Magnitude
		if distance <= chaseRange then
			Humanoid:MoveTo(player.Character.HumanoidRootPart.Position)
		end
	end
	task.wait()
end

This script gets players in the server, compares the distance of the player to the monster, and if that distance is less than chaseRange, the monster will start chasing the player.

It should be perfectly smooth as well as I am using a while loop that only yields until the next Heartbeat. :slight_smile:

1 Like

Thank you ! I’ll use a similar script for some monsters but for complex one, I’ll have to use the PathFindingService.

I think I’ll just exagerate the move to destination of the waypoint and stop the Humanoid when He reached the waypoint.

Hmm I see your problem now - I tried using this code:

local Players = game:GetService("Players")
local Humanoid = script.Parent:FindFirstChild("Humanoid")
local PathFindingService = game:GetService("PathfindingService")
local chaseRange = 15 -- range in studs
while true do
	for _, player in pairs(Players:GetPlayers()) do
		if not player.Character then continue end
		local distance = (player.Character.HumanoidRootPart.Position - script.Parent.HumanoidRootPart.Position).Magnitude
		if distance <= chaseRange then
			local path = PathFindingService:CreatePath()
			path:ComputeAsync(script.Parent.HumanoidRootPart.Position, player.Character.HumanoidRootPart.Position)
			if path.Status == Enum.PathStatus.Success then
				for _, waypoint in pairs(path:GetWaypoints()) do
					Humanoid:MoveTo(waypoint.Position)
					Humanoid.MoveToFinished:Wait()
				end
			end
		end
	end
	task.wait()
end

I am yielding until the humanoid is finished moving to one of the waypoints but it makes it walk in an odd way. This shouldn’t be an issue if the mob is too close to you though; a solution I can think of off the top of my head is to only use the PathFindingService if the monster is far enough away from the player, otherwise just use :MoveTo().

A way to implement this would be pretty simple:

local Players = game:GetService("Players")
local Humanoid = script.Parent:FindFirstChild("Humanoid")
local PathFindingService = game:GetService("PathfindingService")
local chaseRange = 100
local moveToRange = 15 -- range in studs
while true do
	for _, player in pairs(Players:GetPlayers()) do
		if not player.Character then continue end
		local distance = (player.Character.HumanoidRootPart.Position - script.Parent.HumanoidRootPart.Position).Magnitude
		if distance < moveToRange and distance <= chaseRange then
			local path = PathFindingService:CreatePath()
			path:ComputeAsync(script.Parent.HumanoidRootPart.Position, player.Character.HumanoidRootPart.Position)
			if path.Status == Enum.PathStatus.Success then
				for _, waypoint in pairs(path:GetWaypoints()) do
					Humanoid:MoveTo(waypoint.Position)
					Humanoid.MoveToFinished:Wait()
				end
			end
		end
		if distance <= moveToRange then
			Humanoid:MoveTo(player.Character.HumanoidRootPart.Position)
		end
	end
	task.wait()
end

It’ll still look weird when the players are far away, but as you said you have plenty of obstacles, so I assume it won’t be very noticeable.

When the player is close to the monster, it just starts walking directly toward the player smoothly.

1 Like

I already thought about that and I think I’ll use your method. Maybe I’ll use some RayCasting to see when it’s necessary to calculate the path. Thank you !

1 Like

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