Improve npc pathfinding

Hello developers!

I’m currently making a pathfinding monster that steals player’s skin, he walk across waypoints until he sees the player, after that, he’ll start chasing player, but the problem is that the npc is extremly dumb! I’m trying to make a system that when npc lost player, he’ll wander to the last point that the player was seen but there’s some bugs, when the monster kill player, he’ll steals player’s skin

Example:

Here’s his pathfinding script

script.Parent.HumanoidRootPart:SetNetworkOwner(nil)
local monster = script.Parent
local humanoid = monster.Humanoid
local chasing = false
local touch = false
local LostTarget = false
local IsDisguised = false
local insideChase = false

local PathfindingService = game:GetService("PathfindingService")



game.ReplicatedStorage.Remotes.DisguiseSkinStealer.OnServerEvent:Connect(function(plr)
	IsDisguised = true
	local playerId = plr.UserId
	local apparence = game.Players:GetHumanoidDescriptionFromUserId(playerId)
	humanoid:ApplyDescription(apparence)
	monster["Right Arm"].Transparency = 0
	monster["Left Arm"].Transparency = 0
	monster.Torso.Transparency = 0
	monster["Left Leg"].Transparency = 0
	monster["Right Leg"].Transparency = 0
	monster.Head.Transparency = 0
	monster.Body.Transparency = 1
	monster.Eye.Transparency = 1
	monster.Head.face.Transparency = 0
end)

local function canSeeTarget(target)
	local origin = monster.HumanoidRootPart.Position
	local direction = (target.HumanoidRootPart.Position - monster.HumanoidRootPart.Position).Unit * 30
	local ray = Ray.new(origin, direction)
	
	local hit, pos = workspace:FindPartOnRay(ray, monster)
	
	if hit then
		if hit:IsDescendantOf(target) then
			return true
		end
	else 
		return false
	end
end

local function findTarget()
	local players = game.Players:GetPlayers()
	local maxDistance = 300
	local nearestTarget = nil
	
	for index, player in pairs(players) do
		if player.Character and player.Character.Humanoid.Health > 0 then
			local target = player.Character
			local distance = (monster.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
			
			if distance < maxDistance then
				if canSeeTarget(target) or LostTarget == true then
				nearestTarget = target
				maxDistance = distance
				end
				end
		end
	end
	return nearestTarget
end

local function attack(target)
	local distance = (monster.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
	if distance > 4.5 then
		LostTarget = true
		humanoid:MoveTo(target.HumanoidRootPart.Position)
		if script.Parent.Body["HORROR SCREAM 07"].IsPlaying == false then
			script.Parent.Body["HORROR SCREAM 07"]:Play()
		end
		humanoid.MoveToFinished:Connect(function()
			LostTarget = false
		end)
	else
		chasing = false
		if touch == false then
			touch = true
			if IsDisguised == false then
		game.ReplicatedStorage.Remotes.PlayCutscene:FireClient(game.Players:GetPlayerFromCharacter(target), 99, monster, script.AttackAnimation)
			else
				game.ReplicatedStorage.Remotes.PlayCutscene:FireClient(game.Players:GetPlayerFromCharacter(target), 99, monster, script.DAttackAnimation)
				end
			end
		task.wait(3)
		touch = false
	end
end

local function getPath(destination)
	local pathParams = {
		["AgentHeight"] = 4,
		["AgentRadius"] = 4,
		["AgentCanJump"] = false
	}
	
	local path = PathfindingService:CreatePath(pathParams)
	
	path:ComputeAsync(monster.HumanoidRootPart.Position, destination)
	
	return path
end

local 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 then
				if chasing == false then
					chasing = true
				end
				attack(target)

				break
			else
chasing = false
				humanoid:MoveTo(waypoint.Position)
				humanoid.MoveToFinished:Wait()
			end
		end
	else
return
	end
end

local function patrol()
	local waypoints = workspace.waypoints1:GetChildren()
	local randomNum = math.random(1, #waypoints)
	
	--local walkAnim = humanoid:LoadAnimation(script.walk.Animation)
	--walkAnim.Looped = true
	--walkAnim:Play()
	
	if chasing == true then
		if IsDisguised == true then
			humanoid.WalkSpeed = 16
			local RunAnim = humanoid:LoadAnimation(script.walk.RunAnim)
			local WalkAnim = humanoid:LoadAnimation(script.walk.Animation)
			local DRunAnim = humanoid:LoadAnimation(script.walk.DisguisedRun)
			local DWalkAnim = humanoid:LoadAnimation(script.walk.DisguisedWalk)
			if insideChase == false then
				insideChase = true
				RunAnim:Stop()
				DWalkAnim:Stop()
				WalkAnim:Stop()
				DRunAnim:Play()
			end
else
		humanoid.WalkSpeed = 17
		local RunAnim = humanoid:LoadAnimation(script.walk.RunAnim)
		local WalkAnim = humanoid:LoadAnimation(script.walk.Animation)
		RunAnim.Looped = true
		if insideChase == false then
insideChase = true
		RunAnim:Play()
		WalkAnim:Stop()
		end
		end
	else
		if IsDisguised == true then
			humanoid.WalkSpeed = 12
			insideChase = false
			local WalkAnim = humanoid:LoadAnimation(script.walk.RunAnim)
			local RunAnim = humanoid:LoadAnimation(script.walk.Animation)
			local DWalkAnim = humanoid:LoadAnimation(script.walk.DisguisedWalk)
			local DRunAnim = humanoid:LoadAnimation(script.walk.DisguisedRun)
			DWalkAnim:Play()
			RunAnim:Stop()
			DRunAnim:Stop()
			WalkAnim:Stop()
			else
		humanoid.WalkSpeed = 12
		insideChase = false
		local WalkAnim = humanoid:LoadAnimation(script.walk.RunAnim)
		local RunAnim = humanoid:LoadAnimation(script.walk.Animation)
		RunAnim.Looped = true
		RunAnim:Play()
		WalkAnim:Stop()
		end
	end
	
	walkTo(waypoints[randomNum].Position)
end



while wait(0.01) do
		patrol()

end

Here’s the jumpscare script (Not so usefull):

game.ReplicatedStorage.Remotes.PlayCutscene.OnClientEvent:Connect(function(level, monster, anim1)
	local function AvatarAssync()
		local userId = game.Players.LocalPlayer.UserId
		local apparence = game.Players:GetHumanoidDescriptionFromUserId(userId)
		return apparence
	end

	if level == 99 then
			local animend = false
			local cam = game.Workspace.CurrentCamera
			local anim = monster.Humanoid:LoadAnimation(anim1)
anim:Play()
monster.Body["Brief Jumpscare"]:Play()
		monster.Body["HORROR SCREAM 07"]:Stop()
			repeat 
				cam.CameraType = Enum.CameraType.Scriptable
			until cam.CameraType == Enum.CameraType.Scriptable
			game.Players.LocalPlayer.Character.Humanoid.WalkSpeed = 0

			anim.Ended:Connect(function()
				repeat 
					cam.CameraType = Enum.CameraType.Custom
				until	cam.CameraType == Enum.CameraType.Custom
				animend = true
			game.Players.LocalPlayer.Character.Humanoid.Health = 0
				game.Players.LocalPlayer.Character.Humanoid.WalkSpeed = 10
			
			local Apparence = AvatarAssync()
			game.ReplicatedStorage.Remotes.DisguiseSkinStealer:FireServer(Apparence)

			end)
			while not animend do
				cam.CFrame = monster.CameraJumpscare.CFrame
				monster.Humanoid:RemoveAccessories()
				game:GetService("RunService").RenderStepped:Wait()
				task.wait()
			end

His model:

His pathfinding childrens:
Captura de tela 2024-10-17 153131

1 Like

I didn’t read all the code, but it looks like you’re using a position instead of an object when calling MoveTo(). Outside of that, you should probably debug further. For my NPCs, there’s a toggle that shows debug stats next to them like their target, where they’re moving, what their current phase is, ect.

1 Like

Uhhh…

1 Like

Oh, sorry. It’s MoveTo(Position,Instance).

1 Like

If he doesnt move after you get to certain position, its because that, for the PathfindingService, with the parameters that it have, its impossible to the NPC to reach the target

and the function canSeeTarget, the FindPartOnRay is deprecated, i recommend you to use workspace:Raycast()

for example:

local function canSeeTarget(target)
	local origin = monster.HumanoidRootPart.Position
	local direction = (target.HumanoidRootPart.Position - monster.HumanoidRootPart.Position).Unit * 30

	local raycast = workspace:Raycast(origin, direction)

	if raycast then
		if raycast.Instance and raycast.Instance.Parent:FindFirstChild("Humanoid") then
			if target == raycast.Instance.Parent then
				return true
			else
				return false
			end
		else
			return false
		end
	else 
		return false
	end
end
1 Like

Wdym? Could u give me an example

1 Like

I think I’m gonna use mimic AI and make some changes

1 Like