NPC Isn't Chasing Target Player Correctly

Howdy forums,

I’ve been trying to figure out how pathfinding works for a while now, and I can’t seem to nail down how to get a monster npc to wander the map, but when it sees a player to chase them. I’ve tried and the below script is the closest I can get. I’m not sure why but when the target player moves out of the way, the script doesn’t update with the new position of the target player, and Debra (The monster) just slams into the wall. The video and her script is attached below. All help is greatly appreciated, as I’ve exhausted every other resource I could find.

local PathfindingService = game:GetService("PathfindingService")
local Debra = script.Parent
local hum = Debra:WaitForChild("Humanoid")
local currentWaypointIndex = 1
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Ev = ReplicatedStorage.Events.KillCam

for i,v in pairs(script.Parent:GetDescendants()) do
	if v:IsA("Part") or v:IsA("MeshPart") then
		v:SetNetworkOwner(nil)
	end
end

local pathParams = {
	AgentHeight = 9,
	AgentRadius = 5,
	AgentCanJump = false,
	WaypointSpacing = 2,
	Costs = {
		Bricks = 1,
		Plastic = 1,
		SmoothPlastic = 1,
		Wood = 1,
		
	}
}

hum:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
hum:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)

local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude
rayParams.FilterDescendantsInstances = {Debra}

for i,v in pairs(game.Workspace:GetDescendants()) do
	if v.Name == "FirstAidStation" or v.Name == "StorageContainers" then
		rayParams.FilterDescendantsInstances = {v}
	end
end

local lastPos
local status
local Range = 200



local function canSeeTarget(target)
	if target ~= nil then
		local origin = Debra.HumanoidRootPart.Position
		local direction = (target.HumanoidRootPart.Position - Debra.HumanoidRootPart.Position).Unit * Range
		local ray = workspace:Raycast(origin, direction, rayParams)

		if ray and ray.Instance then
			if ray.Instance:IsDescendantOf(target) and not target:FindFirstChild("Hiding") then
				return true
			else
				return false
			end
		else
			return false
		end
	end
end

local function findTarget()
	local players = game.Players:GetChildren()
	local maxDistance = Range
	local nearestTarget

	for i, player in pairs(players) do
		if player.Character then
			local target = player.Character
			local distance = (Debra.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude

			if distance < maxDistance and canSeeTarget(target) then
				nearestTarget = target
				maxDistance = distance
			end
		end
	end

	return nearestTarget
end

local function getPath(destination)
	local path = PathfindingService:CreatePath(pathParams)

	path:ComputeAsync(Debra.HumanoidRootPart.Position, destination.Position)
	
	if path.Status == Enum.PathStatus.NoPath then
		local H = Instance.new("Highlight")
		H.Parent = destination
	end
	
	if path.Status == Enum.PathStatus.Success then
		for i,v in pairs(path:GetWaypoints()) do
			local part = Instance.new("Part")
			part.Material = "Neon"
			part.Size = Vector3.new(0.6,0.6,0.6)
			part.Position =v.Position + Vector3.new(0,2,0)
			part.Anchored = true
			part.CanCollide = false
			part.Parent = game.Workspace.Map
			part.CanTouch = false
		end
	end

	return path
end

local function Attack(target)
	local distance = (Debra.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
	hum.WalkSpeed = 40

	if distance > 15 then
		hum:MoveTo(target.HumanoidRootPart.Position)
	else
		if target:FindFirstChild("Hiding") == nil then
			target.Torso.Anchored = false
			wait()
			Debra.HumanoidRootPart.CFrame = CFrame.new(Debra.HumanoidRootPart.Position,Vector3.new(target.HumanoidRootPart.Position.X,Debra.HumanoidRootPart.Position.Y,target.HumanoidRootPart.Position.Z))
			target.HumanoidRootPart.CFrame = Debra.HumanoidRootPart.CFrame * CFrame.new(0,3,-7.5)
			target.HumanoidRootPart.CFrame = CFrame.new(target.HumanoidRootPart.Position,Vector3.new(Debra.HumanoidRootPart.Position.X,target.HumanoidRootPart.Position.Y,Debra.HumanoidRootPart.Position.Z))
			local DKA = script.Debra
			local PKA = script.Player
			local animTrack1 = hum.Animator:LoadAnimation(DKA)


			if debounce == false then
				debounce = true

				local Player = game.Players:FindFirstChild(target.Name)
				local PlayerNameFolder = target.Name

				local animTrack2 = target.Humanoid.Animator:LoadAnimation(PKA)

				for i, v in pairs(game.Players:GetPlayerFromCharacter(target).Backpack:GetChildren()) do
					if v.Name ~= "Lantern" then
						v.Parent = script.Parent.Parent
						v:MoveTo(target:GetPrimaryPartCFrame().p)
					end
				end
				for i,v in pairs(target:GetChildren()) do
					if v:IsA("Tool") and v.Name ~= "Lantern" then
						v.Parent = workspace
						v:MoveTo(target:GetPrimaryPartCFrame().p)
					end
				end

				for i,v in pairs(target:GetDescendants()) do
					if v:IsA("MeshPart") or v:IsA("Part") then
						v.CanCollide = false
					end
				end

				target.Humanoid.WalkSpeed = 0
				hum.WalkSpeed = 0
				Debra.HumanoidRootPart.Anchored = true
				animTrack1.Priority = Enum.AnimationPriority.Action
				animTrack2.Priority = Enum.AnimationPriority.Action
				Ev:FireClient(Player)
				animTrack2:Play()
				animTrack1:Play()
				wait(3)
				target.Humanoid.Health = 0
				animTrack1.Stopped:Wait()
				Debra.HumanoidRootPart.Anchored = false
				wait(1)
				hum.WalkSpeed = 8
				debounce = nil
				local Grave = game.ReplicatedStorage.Gravestone:Clone()
				Grave.Main.BillboardGui.PlayerName.Text = target.Name
				Grave.Main.SurfaceGui.PlayerName.Text = target.Name
				Grave.Parent = script.Parent.Parent
				wait(0.1)
				Grave.PrimaryPart.CFrame = CFrame.new(target.PrimaryPart.Position.X,target.PrimaryPart.Position.Y - 0.13,target.PrimaryPart.Position.Z)
				Grave:PivotTo(Grave:GetPivot() * CFrame.Angles(math.rad(0), math.random(10, 350), math.rad(0)))
				wait(0.1)
				Grave.PrimaryPart.Anchored = false
				wait(0.25)
				Grave.PrimaryPart.Anchored = true

				if game.ReplicatedStorage.RoundInfo.Players:FindFirstChild(PlayerNameFolder) then
					game.ReplicatedStorage.RoundInfo.Players:FindFirstChild(PlayerNameFolder):Destroy()
				end
			end
		end
	end
end

local function WalkTo(destination)
	local path = getPath(destination)

	if path.Status == Enum.PathStatus.Success then
		for i,waypoint in pairs(path:GetWaypoints()) do
			path.Blocked:Connect(function()
				path:Destroy()
			end)

			local target = findTarget()

			if target and target.Humanoid.Health > 0 then
				lastPos = target.HumanoidRootPart.Position
				Attack(target)
				break
			else
				if lastPos then
					hum:MoveTo(lastPos)
					hum.MoveToFinished:Wait()
					lastPos = nil
					hum.WalkSpeed = 8
					break
				else
					hum:MoveTo(waypoint.Position)
					hum.MoveToFinished:Wait()
				end
			end
		end
	else
		wait(0.25)
		return
	end
end

local function Patrol()
	local waypoints = workspace.Map.Waypoints:GetChildren()
	local ranNum = math.random(1, #waypoints)
	WalkTo(waypoints[ranNum])
end

while task.wait() do
	Patrol()
end

image

I’ve been tyring to solve this all day, and all I’ve managed to do is make it even worse… I have no clue how to solve it, and I desperately need help.

You have a few options. It seems like whatever the pathfinding is seeing is not caught by you.

Option 1:

Check if there are any invisible meshes that covers large areas. Since the pathfinding are able to detect these invisible meshes (that are a part of visible ones) and you are not aware of it, it could have lead to this kind of behaviour.

Option 2:

Do not use pathfinding, but instead manually hardcode the NPC’s control (i.e. if the ray cast hits a block on the left side, then the NPC must go to the right). Might take a while to code them, but it might be worth it.

Option 3:

Do not use pathfinding, and use libraries that allows the NPC to learn from their environments (such as DataPredict). Note that it will take awhile to train, but can become very adaptable to any kind of environment that you throw to the NPC.

1 Like

I recommend the DataPredict library to make this easier, since it’s already made for developers to build functionally generated AI @Ockloe

The only thing is that you have to learn how to use it and integrate it.