NPC Pathfind Target

How to make an NPC Pathfind script that chases players only with a value inside of them set to true?

Below is a section of the pathfinding script inside of the NPC where it chases the player:

function chase()
	while true do
		npchumanoid.WalkSpeed = 20
		npchumanoid.JumpPower = 50
		if not walking and not game.Players:GetPlayerFromCharacter(script.Parent) then
			for i,v in pairs(workspace:GetChildren()) do
				if not v:findFirstChild("Zombie AI") and v:findFirstChildOfClass("Humanoid") and v:findFirstChild("Head") then
					if (v:findFirstChild("Head").Position - npc.Head.Position).magnitude < sight then
						canrandomwalk = false
						local thehumanoid = v:findFirstChildOfClass("Humanoid")
						local pathfinding = false
						local thehead = v:findFirstChild("Head")
						
						while (thehead.Position - npc.Head.Position).magnitude < sight and thehumanoid.Health > 0 and not v:findFirstChild("Zombie AI") do
							npchumanoid.WalkSpeed = 20
							npchumanoid:MoveTo(thehead.Position, thehead)
							local path = game:GetService("PathfindingService"):FindPathAsync(torso.Position, thehead.Position) --find the path from scp's torso to victims head
							local waypoints = path:GetWaypoints() --get the every point of the path
							if path.Status == Enum.PathStatus.Success then
								for q,w in pairs(waypoints) do --for every point existing..
									if q ~= 1 then
										local allow = 0
										npchumanoid:MoveTo(w.Position, thehead) --...walk to it
										while (torso.Position - w.Position).magnitude > 3.8 and allow < 20 do
											allow = allow + 1
											game:GetService("RunService").Heartbeat:wait()
										end
										if w.Action == Enum.PathWaypointAction.Jump then
											npchumanoid.Jump = true
										end
										if thehumanoid.Health <= 0 then
											break
										end
										if v:findFirstChild("Zombie AI") then
											break
										end
									end
								end
								for q,w in pairs(npc:GetChildren()) do
									if w.Name == "pospart" then
										w:destroy()
									end
								end
							else
								npchumanoid:MoveTo(thehead.Position, thehead)
							end
							wait()
						end
						canrandomwalk = true
					else
						canrandomwalk = true
					end
				end
			end
		end
		wait()
	end
end

Many thanks,
u_fep

1 Like

This is not the solution to your NPC pathfinding, but I would like to make your script more reliable and easier to understand:

  1. Since you’re using a loop to find “Zombie AI”, CollectionService will help make it easier to find all of them. For every “Zombie AI”, insert a tag from the properties window and name the tag something like “Zombie”.

In your script, you can get a table of your zombies with this method:

local CollectionService = game.CollectionService
local Zombies = CollectionService:GetTagged("Zombie")
  1. You have 5 different loops nesting within the same function. This is generally a bad practice, I don’t recommend nesting different loops because it’s not performant and reliable. If the PathfindingService runs into an error with the Zombie’s humanoid, your entire script may yield and stop running; something we don’t want for multiple AI. My suggestion is to use RunService’s Heartbeat event and going through the zombie’s functions step by step. I recommend that you read through this carefully and try to understand how the structure really works:
local CollectionService = game.CollectionService
local Zombies: {Model} = CollectionService:GetTagged("Zombie")

local function findTarget()
	local target = nil
	
	-- Your code to getting the target
	
	return target
end

local function moveZombie(zombie: Model, target: Player)
	if target then
		
		-- Your code to moving the zombie to the target.
		
	else
		
		-- Your code to moving the zombie, if there is no target.
		
	end
end

local function attackTarget(zombie: Model, target: Player)
	if target:DistanceFromCharacter(zombie.PrimaryPart.Position) < 10 then
		
		-- Your code to damaging the target when the zombie is in range.
		
	end
end

local function onHeartbeat()
	for i,zombie in Zombies do
		local target = findTarget()
		
		if target then
			moveZombie(zombie, target)
			attackTarget(zombie, target)
		else
			moveZombie(zombie)
		end
	end
end

local RunService = game["Run Service"]
local elapsed = 0

RunService.Heartbeat:Connect(function(deltaTime: number)
	-- deltaTime is the time that passes on a heartbeat.
	-- With elapsed and deltaTime, it will act as a 'wait' for 0.2 seconds.
	-- This way, your code will run
	elapsed += deltaTime
	if elapsed < 0.2 then return end
	elapsed = 0
	
	onHeartbeat()
end)
  1. In your original code, you’re obtaining services like PathfindingService and RunService, which can make your script even slower because you’re constantly calling GetService(). I suggest that you store a variable in your script that refers to these services.

  2. For every iteration that your loop runs through, you’re changing the properties of the zombie, as well as creating variables in your loops. This will slow down your game and your script, as you’re constantly assigning different memory in your computer to different variables. I recommend that you store them outside of the loop, like so:

local CollectionService = game.CollectionService
local Zombies: {Model} = CollectionService:GetTagged("Zombie")
local ZombieInfos = {}

local function getZombieInfo(zombie: Model)
	local ZombieInfo = {
		Humanoid = zombie:FindFirstChildWhichIsA("Humanoid"),
		Head = zombie:FindFirstChildWhichIsA("Head"),
		Target = nil,
		AttackRange = 8,
		-- You can add more variables here, such as animation ids or different walkspeeds.
	}
	
	return ZombieInfo
end

local function setupZombies()
	for i,zombie in Zombies do
		ZombieInfos[zombie] = getZombieInfo(zombie)
		print(ZombieInfos[zombie].AttackRange) -- Outputs 8.
	end
end

I hope that this helps not only you, but to anyone else who happens to be looking for better AI scripting. I’m aware that this may not be the best way to handle AI, but it can be a good start for someone who’s interested in AI scripting.

1 Like

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