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
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.
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
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.
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.
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 !