I’ve written a relatively simple pathfinding script that is supposed to make the NPC chase the player. I’ve done a ton of research on this and apparently pathfinding service is supposed to automatically guide the NPC. However, it wasn’t so I made a ledge detection system but even this doesn’t work. I would really like my NPCs to smartly navigate the maze of platforms to get to the player WITHOUT falling off. Does anyone know how I can achieve this?
Also I would like them to not kill themselves when a player jumps off a ledge to lead them to their death.
-- Services
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")
-- Variables
local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local root = npc:WaitForChild("HumanoidRootPart")
local chaseDistance = script:GetAttribute("ChaseDistance")
local stopDistance = script:GetAttribute("StopDistance")
local damage = script:GetAttribute("Damage")
local attackRange = script:GetAttribute("AttackRange")
local attackCooldown = script:GetAttribute("AttackCooldown")
local weapon = script.Parent.ClassicSword -- Change this to the weapon you want
local toolBindable = script.Parent.ToolBe
local lastAttack = tick()
root:SetNetworkOwner(nil)
local connection
-- Functions
local function ledgeCheck()
local rayStart = root.Position + (root.CFrame.LookVector * 2)
local rayEnd = rayStart - Vector3.new(0, 10, 0)
local ray = Ray.new(rayStart, rayEnd - rayStart)
local hit = workspace:FindPartOnRay(ray)
return not hit
end
function findNearestPlayer()
local playerList = game.Players:GetPlayers()
local targetPlayer = nil
local targetDistance = nil
local targetDirection = nil
local targetPosition = nil
for _, player in pairs(playerList) do
if player.Character then
local hum = player.Character:FindFirstChild("HumanoidRootPart")
if hum then
local distanceVector = player.Character.HumanoidRootPart.Position - root.Position
if not targetPlayer then
targetPlayer = player
targetDistance = distanceVector.Magnitude
targetDirection = distanceVector.Unit
targetPosition = player.Character.HumanoidRootPart.Position
elseif distanceVector.Magnitude < targetDistance then
targetPlayer = player
targetDistance = distanceVector.Magnitude
targetDirection = distanceVector.Unit
targetPosition = player.Character.HumanoidRootPart.Position
end
end
end
end
return targetPlayer, targetDistance, targetDirection, targetPosition
end
-- Events
connection = RunService.Heartbeat:Connect(function()
local targetPlayer, targetDistance, targetDirection, targetPosition = findNearestPlayer()
if targetPlayer then
-- Check if target is within chase distance
if targetDistance <= chaseDistance then
humanoid:EquipTool(weapon)
-- Check if target is within stop distance
if targetDistance >= stopDistance then
local path = PathfindingService:FindPathAsync(root.Position, targetPosition)
if path.Status == Enum.PathStatus.Success then
local waypoints = path:GetWaypoints()
if humanoid.FloorMaterial ~= Enum.Material.Air and not ledgeCheck() then
for _, waypoint in ipairs(waypoints) do
local waypointPosition = waypoint.Position
local waypointAction = waypoint.Action
if waypointAction == Enum.PathWaypointAction.Jump then
humanoid.Jump = true
end
humanoid:MoveTo(waypointPosition)
end
end
end
else
humanoid:MoveTo(root.Position)
end
-- Check if target is within attack range
if targetDistance <= attackRange and tick() - lastAttack >= attackCooldown then
-- animation here
lastAttack = tick()
local targetHumanoid = targetPlayer.Character:FindFirstChildOfClass("Humanoid")
if targetHumanoid and targetHumanoid.Health > 0 then
toolBindable:Fire()
print("fired")
end
end
else
humanoid:UnequipTools()
end
else
humanoid:MoveTo(root.Position)
end
end)
humanoid.Died:Connect(function()
if humanoid.Health == 0 then
print(`Destroying {script.Parent.Name}...`)
wait(1)
connection:Disconnect()
script.Parent:Destroy()
end
end)