I am making the AI for the monster in my horror maze game. It works fine, but it follows the waypoint instead of chasing me when I get close. And the AI keeps bumping into walls when they can choose a longer way to get to that waypoint without bumping in walls.
Here is the video recording, the photo of all waypoints in the maze, and the AI Script:
local PathfindingService = game:GetService("PathfindingService") local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local monster = script.Parent local monsterHumanoid = monster:WaitForChild("Humanoid") local monsterWaypoints = workspace:WaitForChild("MonsterWaypoints") local monsterRoot = monster:WaitForChild("HumanoidRootPart") for index, bodyPart in pairs(monster:GetDescendants()) do if bodyPart:IsA("BasePart") then bodyPart:SetNetworkOwner(nil) end end local function CheckIfAlive(character) if ((character ~= nil) and (Players:GetPlayerFromCharacter(character) ~= nil) and (character:FindFirstChild("Head") ~= nil) and (character:FindFirstChild("HumanoidRootPart") ~= nil) and (character:FindFirstChildOfClass("Humanoid") ~= nil) and ((character:FindFirstChildOfClass("Humanoid")).Health > 0)) then return true end end local function CanSeeTarget(target) local origin = monsterRoot.Position local direction = (target:WaitForChild("HumanoidRootPart").Position - monsterRoot.Position).Unit * 40 local ray = Ray.new(origin, direction) local hit, pos = workspace:FindPartOnRay(ray, monster) if (hit ~= nil) then if hit:IsDescendantOf(target) then return true end else return false end end local function FindTarget() local maxDistance = 40 local nearestTarget for index, player in pairs(Players:GetPlayers()) do local character = player.Character or player.CharacterAdded:Wait() if (character ~= nil) then local target = character local distance = (monsterRoot.Position - character:WaitForChild("HumanoidRootPart").Position).Magnitude if (distance < maxDistance) and (CanSeeTarget(target) == true) then nearestTarget = target maxDistance = distance end end end return nearestTarget end local function GetPath(destination) local agentParameters = { ["AgentHeight"] = 5, ["AgentRadius"] = 4, ["AgentCanJump"] = false, ["Costs"] = { ["SafeSpot"] = math.huge } } local path = PathfindingService:CreatePath(agentParameters) path:ComputeAsync(monsterRoot.Position, destination.Position) return path end function Attack(target) local distance = (monsterRoot.Position - target:WaitForChild("HumanoidRootPart").Position).Magnitude if distance > 4 then monsterHumanoid:MoveTo(target:WaitForChild("HumanoidRootPart").Position) else target:FindFirstChildOfClass("Humanoid"):TakeDamage(target:FindFirstChildOfClass("Humanoid").MaxHealth) end end function WalkTo(destination) local path = GetPath(destination) if path.Status == Enum.PathStatus.Success then for index, waypoint in pairs(path:GetWaypoints()) do local target = FindTarget() if (target ~= nil) and (CheckIfAlive(target) == true) then Attack(target) break else monsterHumanoid:MoveTo(waypoint.Position) monsterHumanoid.MoveToFinished:Wait() end end else monsterHumanoid:MoveTo((destination.Position - (monsterRoot.CFrame.LookVector * 10))) end end function Patrol() local waypoints = monsterWaypoints:GetChildren() local randomWaypoint = waypoints[math.random(1, #waypoints)] WalkTo(randomWaypoint) end while true do task.wait() Patrol() end
I finally fixed it! @Puppynniko suggested detecting if the target is nill or not in the patrol function, and it worked! Thanks to @Puppynniko! This is my new AI script:
local PathfindingService = game:GetService("PathfindingService") local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local monster = script.Parent local monsterHumanoid = monster:WaitForChild("Humanoid") local monsterWaypoints = workspace:WaitForChild("MonsterWaypoints") local monsterRoot = monster:WaitForChild("HumanoidRootPart") local chasing = false local hipHeight = monsterHumanoid.HipHeight local HitboxLength = 4 local agentParameters = { ["AgentHeight"] = ((hipHeight > 0 and hipHeight) or 5), ["AgentRadius"] = (monsterRoot.Size.X or 2), ["AgentCanJump"] = false, ["Costs"] = { ["SafeSpot"] = math.huge } } for index, bodyPart in pairs(monster:GetDescendants()) do if bodyPart:IsA("BasePart") or bodyPart:IsA("MeshPart") or bodyPart:IsA("UnionOperation") then bodyPart:SetNetworkOwner(nil) end end local function CheckIfAlive(character) if ((character ~= nil) and (Players:GetPlayerFromCharacter(character) ~= nil) and (character:FindFirstChild("Head") ~= nil) and (character:FindFirstChild("HumanoidRootPart") ~= nil) and (character:FindFirstChildOfClass("Humanoid") ~= nil) and ((character:FindFirstChildOfClass("Humanoid")).Health > 0)) then return true else return false end end local function CheckIfTargetIsSafe(target) if (target ~= nil) and (Players:GetPlayerFromCharacter(target) ~= nil) and (target:FindFirstChild("Head") ~= nil) and (target:FindFirstChild("HumanoidRootPart") ~= nil) and (target:FindFirstChildOfClass("Humanoid") ~= nil) then if Players:GetPlayerFromCharacter(target):FindFirstChild("Safe") then if Players:GetPlayerFromCharacter(target):FindFirstChild("Safe").Value == true then return true else if target:FindFirstChildOfClass("ForceField") then return true else return false end end else if target:FindFirstChildOfClass("ForceField") then return true else return false end end else return false end end local function CanSeeTarget(target) local origin = monsterRoot.Position local direction = (target:WaitForChild("HumanoidRootPart").Position - monsterRoot.Position).Unit * 1000 local ray = Ray.new(origin, direction) local hit, pos = workspace:FindPartOnRayWithIgnoreList(ray, {monster}) if hit then if hit:IsDescendantOf(target) then if target:FindFirstChildOfClass("ForceField") then return true else local plr = Players:GetPlayerFromCharacter(target) if plr then local safeValue = plr:FindFirstChild("Safe") if safeValue then if safeValue.Value == false then return true else return false end else return false end else return false end end end else return false end end local function FindTarget() local maxDistance = 10000 local nearestTarget for index, player in pairs(Players:GetPlayers()) do local character = player.Character or player.CharacterAdded:Wait() if character then local target = character local distance = (monsterRoot.Position - character:WaitForChild("HumanoidRootPart").Position).Magnitude if (distance < maxDistance) and (CanSeeTarget(target) == true) then nearestTarget = target maxDistance = distance end end end return nearestTarget end local function GetPath(destination) local path = PathfindingService:CreatePath(agentParameters) path:ComputeAsync(monsterRoot.Position, destination.Position) return path end function Attack(target) local distance = (monsterRoot.Position - target:WaitForChild("HumanoidRootPart").Position).Magnitude if distance > (HitboxLength or 1) then monsterHumanoid:MoveTo(target:WaitForChild("HumanoidRootPart").Position) else target:FindFirstChildOfClass("Humanoid"):TakeDamage(target:FindFirstChildOfClass("Humanoid").MaxHealth) end end function WalkTo(destination) local path = GetPath(destination) if path.Status == Enum.PathStatus.Success then for index, waypoint in pairs(path:GetWaypoints()) do local target = FindTarget() if target and CanSeeTarget(target) and (CheckIfAlive(target) == true) and (CheckIfTargetIsSafe(target) == false) then break else monsterHumanoid:MoveTo(waypoint.Position) monsterHumanoid.MoveToFinished:Wait() end end else monsterHumanoid:MoveTo((destination.Position - (monsterRoot.CFrame.LookVector * 10))) end end function Patrol() local target = FindTarget() if target and CanSeeTarget(target) and (CheckIfAlive(target) == true) and (CheckIfTargetIsSafe(target) == false) then Attack(target) else local waypoints = monsterWaypoints:GetChildren() local randomWaypoint = waypoints[math.random(1, #waypoints)] WalkTo(randomWaypoint) end end while true do task.wait() Patrol() end
Also, you can use this script too to make your own Monster AI! Don't exactly copy-paste mine because it could cause errors.
If you're wondering what game it is, here's the game!