I recently created a game on Roblox but I have reached a problem during development. It seems like the Monster NPC are stopping momentarily before moving to the next waypoint which is frustrating since It makes the NPC too slow to attack players.
Here is a video:
Here is the code:
local Monster = script.Parent
local MHumanoid = Monster:FindFirstChildOfClass("Humanoid")
local myRoot = Monster.HumanoidRootPart
local IgnoreParent = Monster.Parent
local Hitbox = Monster.Hitbox
local AttackDelay = Monster:GetAttribute("AttackDelay")
local Damage = Monster:GetAttribute("Damage")
local NoticeDistance = Monster:GetAttribute("NoticeDistance")
local Animator = MHumanoid:FindFirstChildOfClass("Animator")
local goal = Monster.HumanoidRootPart
local Animations = {
["AttackAnimation"] = 18616845543
}
local path = game:GetService("PathfindingService"):CreatePath({
AgentRadius = 2,
AgentHeight = 5,
AgentCanJump = true,
AgentJumpHeight = 7.2,
AgentCanClimb = true
})
local Chasing = false
local Attacking = false
Monster:GetAttributeChangedSignal("NoticeDistance"):Connect(function()
NoticeDistance = Monster:GetAttribute("NoticeDistance")
end)
Monster:GetAttributeChangedSignal("Damage"):Connect(function()
Damage = Monster:GetAttribute("Damage")
end)
Monster:GetAttributeChangedSignal("AttackDelay"):Connect(function()
AttackDelay = Monster:GetAttribute("AttackDelay")
end)
--// Services
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ServerStorage = game:GetService("ServerStorage")
Monster.PrimaryPart:SetNetworkOwner(nil)
-- Function to find the nearest player
local function findNearestPlayer()
local nearestPlayer = nil
local shortestDistance = math.huge
for _, player in ipairs(Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChild("HumanoidRootPart") then
--if isTargetVisible(character.HumanoidRootPart) then
local distance = (character.HumanoidRootPart.Position - Monster.HumanoidRootPart.Position).Magnitude
if distance < shortestDistance then
nearestPlayer = character
shortestDistance = distance
end
-- end
end
end
return nearestPlayer
end
local function PlayAnimation(id, duration)
if type(id) == "string" then
id = tonumber(id)
end
local Animation = Instance.new("Animation", Monster)
Animation.AnimationId = "rbxassetid://"..tostring(id)
local anim = Animator:LoadAnimation(Animation)
local speed = 1
if duration then
speed = duration / anim.Length
end
anim:Play(0, 1, speed)
anim.Ended:Connect(function()
Animation:Destroy()
end)
local a = duration or 10
task.spawn(function()
task.wait(a)
Animation:Destroy()
end)
end
coroutine.wrap(function()
-- Attack
while MHumanoid.Health > 0 do
task.wait(AttackDelay)
local oOverlapParams = OverlapParams.new()
oOverlapParams.FilterDescendantsInstances = {IgnoreParent}
oOverlapParams.FilterType = Enum.RaycastFilterType.Exclude
local Parts = workspace:GetPartsInPart(Hitbox, oOverlapParams)
if #Parts >= 1 then
for i, part in pairs(Parts) do
if Players:GetPlayerFromCharacter(part.Parent) then
local Char:Model = part.Parent
if part.Name == "HumanoidRootPart" or Char.PrimaryPart == part then
local PHum = Char:FindFirstChildOfClass("Humanoid")
if PHum.Health > 0 then
PHum:TakeDamage(Damage)
task.spawn(PlayAnimation, Animations.AttackAnimation, Random.new():NextNumber(0.2,0.3))
Monster.Head.Attack:Play()
end
end
end
end
end
end
end)()
function checkSight(target)
local ray = Ray.new(myRoot.Position, (target.Position - myRoot.Position).Unit * 40)
local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent})
if hit then
if hit:IsDescendantOf(target.Parent) and math.abs(hit.Position.Y - myRoot.Position.Y) < 3 then
return true
end
end
return false
end
function findPath(target)
path:ComputeAsync(myRoot.Position,target.Position)
local waypoints = path:GetWaypoints()
if path.Status == Enum.PathStatus.Success then
for _, waypoint in ipairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
MHumanoid.Jump = true
end
MHumanoid:MoveTo(waypoint.Position)
local timeOut = MHumanoid.MoveToFinished:Wait(1)
if not timeOut then
MHumanoid.Jump = true
findPath(target)
break
end
if checkSight(target) then
repeat
MHumanoid:MoveTo(target.Position)
task.wait(0.1)
if target == nil then
break
elseif target.Parent == nil then
break
end
until checkSight(target) == false or MHumanoid.Health == 0 or target.Parent.Humanoid.Health == 0
break
end
if (myRoot.Position - waypoints[1].Position).magnitude > 15 then
findPath(target)
break
end
end
end
end
coroutine.wrap(function()
while task.wait(0.1) do
if MHumanoid.Health <= 0 then
break
end
local target = findNearestPlayer()
if target then
findPath(target.HumanoidRootPart)
end
end
end)()
coroutine.wrap(function()
while task.wait(1) do
if Chasing then
local Chance = math.random(1, 20)
if Chance >= 15 then
if Chasing == true then
Monster.Head.Scream:Play()
end
end
end
end
end)()
Try changing the refresh time for the pathfinding, if this still doesn’t work let me know!
local RunService = game:GetService("RunService")
coroutine.wrap(function()
local connection
connection = RunService.Heartbeat:Connect(function()
if MHumanoid.Health <= 0 then
connection:Disconnect()
end
local target = findNearestPlayer()
if target then
findPath(target.HumanoidRootPart)
end
end)
end)()
Well, that’s because you aren’t in the game right, isn’t the code attempting to find you, or do are they meant to have like pathfinding places where they just walk around, because if they have places they are meant to walk around then they should be doing that. Show me in the actual game, maybe it won’t fix anything but I am curious
How do you have organized this system? You are doing like a “handler” that updates every AI with a single repeat loop (while or renderstepped) or every model has a individual script?, if so, have you checked if this is a web problem?, like, if there are multiple instances of this AI running at the same time, they are slowing down the server in general, is receiving so much data from the pathfinding instances that it can’t handle it all at the same time, doing that slowness.
local Monster = script.Parent
local MHumanoid = Monster:FindFirstChildOfClass("Humanoid")
local myRoot = Monster.HumanoidRootPart
local IgnoreParent = Monster.Parent
local Hitbox = Monster.Hitbox
local AttackDelay = Monster:GetAttribute("AttackDelay")
local Damage = Monster:GetAttribute("Damage")
local NoticeDistance = Monster:GetAttribute("NoticeDistance")
local Animator = MHumanoid:FindFirstChildOfClass("Animator")
local goal = Monster.HumanoidRootPart
local Animations = {
["AttackAnimation"] = 18616845543
}
local path = game:GetService("PathfindingService"):CreatePath({
AgentRadius = 2,
AgentHeight = 2,
AgentCanJump = true,
AgentJumpHeight = 7.2,
AgentCanClimb = true
})
local Chasing = false
local Attacking = false
Monster:GetAttributeChangedSignal("NoticeDistance"):Connect(function()
NoticeDistance = Monster:GetAttribute("NoticeDistance")
end)
Monster:GetAttributeChangedSignal("Damage"):Connect(function()
Damage = Monster:GetAttribute("Damage")
end)
Monster:GetAttributeChangedSignal("AttackDelay"):Connect(function()
AttackDelay = Monster:GetAttribute("AttackDelay")
end)
--// Services
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ServerStorage = game:GetService("ServerStorage")
Monster.PrimaryPart:SetNetworkOwner(nil)
-- Function to find the nearest player
local function findNearestPlayer()
local nearestPlayer = nil
local shortestDistance = math.huge
for _, player in ipairs(Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChild("HumanoidRootPart") then
--if isTargetVisible(character.HumanoidRootPart) then
local distance = (character.HumanoidRootPart.Position - Monster.HumanoidRootPart.Position).Magnitude
if distance < shortestDistance then
nearestPlayer = character
shortestDistance = distance
end
-- end
end
end
return nearestPlayer
end
local function PlayAnimation(id, duration)
if type(id) == "string" then
id = tonumber(id)
end
local Animation = Instance.new("Animation", Monster)
Animation.AnimationId = "rbxassetid://"..tostring(id)
local anim = Animator:LoadAnimation(Animation)
local speed = 1
if duration then
speed = duration / anim.Length
end
anim:Play(0, 1, speed)
anim.Ended:Connect(function()
Animation:Destroy()
end)
local a = duration or 10
task.spawn(function()
task.wait(a)
Animation:Destroy()
end)
end
coroutine.wrap(function()
-- Attack
while MHumanoid.Health > 0 do
task.wait(AttackDelay)
local oOverlapParams = OverlapParams.new()
oOverlapParams.FilterDescendantsInstances = {IgnoreParent}
oOverlapParams.FilterType = Enum.RaycastFilterType.Exclude
local Parts = workspace:GetPartsInPart(Hitbox, oOverlapParams)
if #Parts >= 1 then
for i, part in pairs(Parts) do
if Players:GetPlayerFromCharacter(part.Parent) then
local Char:Model = part.Parent
if part.Name == "HumanoidRootPart" or Char.PrimaryPart == part then
local PHum = Char:FindFirstChildOfClass("Humanoid")
if PHum.Health > 0 then
PHum:TakeDamage(Damage)
task.spawn(PlayAnimation, Animations.AttackAnimation, Random.new():NextNumber(0.2,0.3))
Monster.Head.Attack:Play()
end
end
end
end
end
end
end)()
function checkSight(target)
local ray = Ray.new(myRoot.Position, (target.Position - myRoot.Position).Unit * 40)
local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent})
if hit then
if hit:IsDescendantOf(target.Parent) and math.abs(hit.Position.Y - myRoot.Position.Y) < 3 then
return true
end
end
return false
end
function findPath(target)
path:ComputeAsync(myRoot.Position, target.Position)
local waypoints = path:GetWaypoints()
if path.Status == Enum.PathStatus.Success then
for _, waypoint in ipairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
MHumanoid.Jump = true
end
MHumanoid:MoveTo(waypoint.Position)
local timeOut = MHumanoid.MoveToFinished:Wait()
if not timeOut then
MHumanoid.Jump = true
findPath(target)
break
end
if checkSight(target) then
repeat
MHumanoid:MoveTo(target.Position)
task.wait(0.1)
if target == nil then
break
elseif target.Parent == nil then
break
end
until checkSight(target) == false or MHumanoid.Health == 0 or target.Parent.Humanoid.Health == 0
break
end
if (myRoot.Position - waypoint.Position).magnitude > 15 then
findPath(target)
break
end
end
end
end
coroutine.wrap(function()
while task.wait(0.1) do
if MHumanoid.Health <= 0 then
break
end
local target = findNearestPlayer()
if target then
findPath(target.HumanoidRootPart)
end
end
end)()
coroutine.wrap(function()
while task.wait(1) do
if Chasing then
local Chance = math.random(1, 20)
if Chance >= 15 then
if Chasing == true then
Monster.Head.Scream:Play()
end
end
end
end
end)()