NPC Cant Catch Up To Player

So I have an NPC which moves around and when it gets close enough to a player it chases them, if it gets very close to the player it will start attacking.
However there is one problem; when the npc gets close to the player it stops for a second like this:


im not the best at pathfinding but here is my code:

local pathfindingService = game:GetService("PathfindingService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local foundtarget = nil
local targetpos = nil
local humanoid = script.Parent.Humanoid
local humanoidrootpart = script.Parent.HumanoidRootPart
local body = script.Parent:FindFirstChild("Torso")
local stunned = script.Parent.Stunned
local destination = game.Workspace.CampingPath:GetChildren()

-- << Initial Setup >> 
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)
local Char = body.Parent
local Params = RaycastParams.new()
Params.FilterType = Enum.RaycastFilterType.Blacklist
Params.FilterDescendantsInstances = {Char} --- remember to define our character!

local newHitbox = RaycastHitbox.new(script.Parent.Knife)
newHitbox.RaycastParams = Params
RaycastHitbox.DetectionMode = 1
task.wait()


local Cooldown = .75
local HitDebounce = false
local SwingDebounce = false
local AttackDebounce = false
local MaxAttackDistance = 5

local SwingSounds = Char.AttackZone.SwingSounds:GetChildren()
local HitSounds = Char.AttackZone.HitSounds:GetChildren()




newHitbox.OnHit:Connect(function(hit, humanoid, hum) 
	if HitDebounce == false then
		HitDebounce = true
		local RandomHitSound = HitSounds[math.random(1, #HitSounds)]
		print(humanoid)
		RandomHitSound:Play()
		humanoid:TakeDamage(10)
		print("hit")
		newHitbox:HitStop()
		task.wait(Cooldown)
		HitDebounce = false
	end
end)	




local IdleAnim = humanoid.Animator:LoadAnimation(script.Parent.Idle)
local WalkAnim = humanoid.Animator:LoadAnimation(script.Parent.Walk)
local RunAnim = humanoid.Animator:LoadAnimation(script.Parent.Run)
local Attack1Anim = humanoid.Animator:LoadAnimation(script.Parent.Attack)
local Attack2Anim = humanoid.Animator:LoadAnimation(script.Parent.Attack2)

Attack1Anim.Stopped:Connect(function()
	newHitbox:HitStop()
end)
Attack2Anim.Stopped:Connect(function()
	newHitbox:HitStop()
end)

local blockedConnection


humanoidrootpart:SetNetworkOwner(nil)
local points = {}
for _,part in ipairs(destination) do
	table.insert(points,part)
	task.wait()
end

foundtarget = nil

function walkRandomly()
	foundtarget = nil
	local point = points[math.random(1,#points)]
	local target = FindTarget()
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 2,
		AgentHeight = 5,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position, point.Position)
	local waypoints = path:GetWaypoints()
	for _, waypoint in ipairs(waypoints) do
		if foundtarget == nil then
			print("DEBUG: WALKING RANDOMLY TO WAYPOINT")
		humanoid:MoveTo(waypoint.Position)
		path.Blocked:Connect(function()
			print("DEBUG: PATH BLOCKED (WALKRANDOMLY)")
			main()
		end)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		end
		if foundtarget == 1 then path:Destroy() break end
		humanoid.MoveToFinished:Wait()
		FindTarget()
	end
end
--[[
local path = game:GetService("PathfindingService"):CreatePath({
	AgentRadius = 2,
	AgentHeight = 5,
	AgentCanJump = false
})
--]]
local function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 2,
		AgentHeight = 5,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	local waypoints = path:GetWaypoints()

	for _, waypoint in ipairs(waypoints) do
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				path.Blocked:Connect(function()
					print("DEBUG: PATH BLOCKED (findPath")
					findPath(target)
				end)
				task.wait()
				if target == nil then
					break
				elseif target.Parent == nil then
					break
				end
			until checkSight(target) == false
			if not stunned.Value then
			humanoidrootpart:SetNetworkOwner(nil)
			end
			print("DEBUG: LOST TARGET, DESTROYING PATH (findPath)")
		--	foundtarget = nil
			break
		else
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
			end
			humanoid:MoveTo(waypoint.Position)
			local waitCheck = humanoid.MoveToFinished:Wait(1)
			if not waitCheck then
				findPath(target)
				break
			end

			if (humanoidrootpart.Position - waypoints[1].Position).Magnitude > 30 then
				print("DEBUG: FINDPATH(TARGET) (findPath)")
				
				findPath(target)
				break
			end
		end
	end
end

--[[
function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	local waypoints = path:GetWaypoints()

	for _, waypoint in ipairs(waypoints) do
		humanoid:MoveTo(waypoint.Position)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				path.Blocked:Connect(function()
					print("DEBUG: PATH BLOCKED (findPath")
				main()
				end)
				task.wait()
				if target == nil then
					break
				elseif target.Parent == nil then
					break
				end
			until checkSight(target) == false
			print("DEBUG: LOST TARGET, DESTROYING PATH (findPath)")
			foundtarget = nil
			path:Destroy()
			break
		end

		if (humanoidrootpart.Position - waypoints[1].Position).Magnitude > 30 then
			print("DEBUG: FINDPATH(TARGET) (findPath)")
			findPath(target)
			break
		end
	end
end
--]]

function FindTarget()
	local dist = 20
	local target
	local players = game.Players:GetPlayers()
	for i,v in ipairs(players) do
		if (v.Character) then
			local torso = v.Character.Torso
			local human = v.Character.Humanoid
			if (humanoidrootpart.Position - torso.Position).Magnitude < dist and human.Health > 0 then
				if checkSight(torso) and human.Health ~= 0 then
					print("DEBUG: TARGET = TORSO (FindTarget)")
					print(human.Health)
					target = torso 
					
				end
			end
		end
	end
	return target
end

function checkSight(target)
	local ray = Ray.new(humanoidrootpart.Position, (target.Position - humanoidrootpart.Position).Unit * 50)
	local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent}) 
	local Player = game:GetService("Players"):GetPlayerFromCharacter(target.Parent)
	if hit then
		if hit:IsDescendantOf(target.Parent) then
			if not stunned.Value then
			--humanoidrootpart:SetNetworkOwner(target)
			end
			--[[
			if not targetDebounce then
			targetDebounce = true
			FraudFoundPlayer:FireClient(Player)
			task.wait(1)
			targetDebounce = false
			end
			--]]
			--[[]]
			if (humanoidrootpart.Position - target.Position).Magnitude < MaxAttackDistance then
				--print("Attack")
				local RandomAttackAnim = math.random(1,2)
				if RandomAttackAnim == 1 then
					if Attack1Anim.IsPlaying == false and Attack2Anim.IsPlaying == false and not SwingDebounce then 
					SwingDebounce = true
					Attack1Anim:Play()

					newHitbox:HitStart()
					
					local RandomSwingSound = SwingSounds[math.random(1, #SwingSounds)]
					RandomSwingSound:Play()
					
					task.wait(.75)
					SwingDebounce = false
					end
					end
					--[[
				else
					if Attack2Anim.IsPlaying == false and Attack1Anim.IsPlaying == false and not SwingDebounce then
						SwingDebounce = true
					Attack2Anim:Play()
					print("Attack2")
					if Attack1Anim.IsPlaying == true then
					newHitbox:HitStart()
					local RandomSwingSound = SwingSounds[math.random(1, #SwingSounds)]
					RandomSwingSound:Play()
					end
					task.wait(.75)
					SwingDebounce = false
					end
				end
				--]]

			end
			--print("DEBUG: FOUND TARGET (checkSight)")
			foundtarget = 1
			return true
		end
	end
	return false
end


function main()
	local target = FindTarget()
	if target and foundtarget == 1 then
		print(target)
		local Player = game.Players:GetPlayerFromCharacter(target.Parent)
		humanoid.WalkSpeed = 18
		findPath(target)

	else
		foundtarget = nil
		humanoid.WalkSpeed = 7
		walkRandomly()
	end
end

humanoid.Running:Connect(function(speed)
	if speed > 17 then
		IdleAnim:Stop()
		WalkAnim:Stop()
		RunAnim:Play()
	elseif speed > 4 and speed < 17 then
		IdleAnim:Stop()
		WalkAnim:Play()
		RunAnim:Stop()
	elseif speed <= 4 then
		IdleAnim:Play()
		WalkAnim:Stop()
		RunAnim:Stop()
	end

end)

while RunService.Heartbeat:Wait() do
	main() 
end
4 Likes

Path computation takes roughly a second or so, it’s asynchronous so it’s not immediate.

One thing I planned on implementing for my pathfinding controller was a check to determine if the player is in direct line of sight of the NPC’s HumanoidRootPart using raycasting. If so, use MoveTo directly to move the NPC to the player instead of unnecessarily computing a path to do so.

Kind of like what you already do for your attack detection.

Hope it helps.

4 Likes

Could you give me an example of how I should raycast towards the player to see if they are walking straight? I kinda do that in the checkSight function

You did it exactly right.

If it hits the player, there’s a straight line between the NPC’s root part to the player. If not, compute a new path.

Is it the Cooldown in the OnHit function?
I’m just guessing since the video only shows pauses while the NPC attacks. As a quick check maybe try taking out the call to attack the player and see if the pathfinding still pauses.

I actually did and it had the same result

1 Like

So in my function

function checkSight(target)
	local ray = Ray.new(humanoidrootpart.Position, (target.Position - humanoidrootpart.Position).Unit * 50)
	local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent}) 
	local Player = game:GetService("Players"):GetPlayerFromCharacter(target.Parent)
	if hit then
		if hit:IsDescendantOf(target.Parent) then
			if not stunned.Value then
			humanoidrootpart:SetNetworkOwner(target)
			end
			if (humanoidrootpart.Position - target.Position).Magnitude < MaxAttackDistance then
				--print("Attack")
				local RandomAttackAnim = math.random(1,2)
				if RandomAttackAnim == 1 then
					if Attack1Anim.IsPlaying == false and Attack2Anim.IsPlaying == false and not SwingDebounce then 
					SwingDebounce = true
					Attack1Anim:Play()

					newHitbox:HitStart()
					
					local RandomSwingSound = SwingSounds[math.random(1, #SwingSounds)]
					RandomSwingSound:Play()
					
					task.wait(.75)
					SwingDebounce = false
					end
					end
	
			end
			--print("DEBUG: FOUND TARGET (checkSight)")
			foundtarget = 1
			return true
		end
	end
	return false
end

the hit detects if it can see the player and its not being the wall, then it checks if the hit was a part of the player and if it was, it just does my attack code, changes the foundtarget = 1, and returns true.
So are you saying I could make a new function that just walks toward to player without pathfinding using MoveTo, and when I do that if hit:IsDescendantOf(target.Parent) then i could add an else statement that would call the MoveTo? I feel like im confusing myself here cause the checkSight function just checks if the player isnt behind a wall.

1 Like

I was just referring to your raycast code. But yeah that’s basically the idea I had. If the ray hits the player, a line was successfully formed between the player and the NPC, meaning the NPC is able to traverse in a straight line directly towards the player (no need for PathfindingService.)

If the ray hit something other than a player, path computation would be needed so the NPC navigates around anything the ray hit that wasn’t the player.

1 Like

I understand now but im kinda confused and how I should put that into my code, this is what I think (Correct me if im wrong)

in the if hit:IsDescendantOf(target.Parent) then line, i add an else statement, and in the else statement I call the findPath function

i make a MoveTo(target) function that just normally moves towards the player, and then I call the MoveTo() function in the if hit:IsDescendantOf(target.Parent) then line? ( i think this is the part im getting wrong) also feel free to edit the code I posted above

You can also trying reading throughout my code to understand what it does better @C_Sharper Thanks!!!

also on the side, here is the code im using right now (not sure if you need this)

local pathfindingService = game:GetService("PathfindingService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local foundtarget = nil
local targetpos = nil
local humanoid = script.Parent.Humanoid
local humanoidrootpart = script.Parent.HumanoidRootPart
local body = script.Parent:FindFirstChild("Torso")
local stunned = script.Parent.Stunned
local destination = game.Workspace.CampingPath:GetChildren()

-- << Initial Setup >> 
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)
local Char = body.Parent
local Params = RaycastParams.new()
Params.FilterType = Enum.RaycastFilterType.Blacklist
Params.FilterDescendantsInstances = {Char} --- remember to define our character!

local newHitbox = RaycastHitbox.new(script.Parent.Knife)
newHitbox.RaycastParams = Params
RaycastHitbox.DetectionMode = 1
task.wait()


local Cooldown = .75
local HitDebounce = false
local SwingDebounce = false
local AttackDebounce = false
local MaxAttackDistance = 5
local WalkStraight = false

local SwingSounds = Char.AttackZone.SwingSounds:GetChildren()
local HitSounds = Char.AttackZone.HitSounds:GetChildren()



--
newHitbox.OnHit:Connect(function(hit, humanoid, hum) 
	if HitDebounce == false then
		HitDebounce = true
		local RandomHitSound = HitSounds[math.random(1, #HitSounds)]
		print(humanoid)
		RandomHitSound:Play()
		humanoid:TakeDamage(10)
		print("hit")
		newHitbox:HitStop()
		task.wait(Cooldown)
		HitDebounce = false
	end
end)	
--



local IdleAnim = humanoid.Animator:LoadAnimation(script.Parent.Idle)
local WalkAnim = humanoid.Animator:LoadAnimation(script.Parent.Walk)
local RunAnim = humanoid.Animator:LoadAnimation(script.Parent.Run)
local Attack1Anim = humanoid.Animator:LoadAnimation(script.Parent.Attack)
local Attack2Anim = humanoid.Animator:LoadAnimation(script.Parent.Attack2)

Attack1Anim.Stopped:Connect(function()
	newHitbox:HitStop()
end)
Attack2Anim.Stopped:Connect(function()
	newHitbox:HitStop()
end)

local blockedConnection


humanoidrootpart:SetNetworkOwner(nil)
local points = {}
for _,part in ipairs(destination) do
	table.insert(points,part)
	task.wait()
end

foundtarget = nil

function walkRandomly()
	foundtarget = nil
	local point = points[math.random(1,#points)]
	local target = FindTarget()
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 2,
		AgentHeight = 5,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position, point.Position)
	local waypoints = path:GetWaypoints()
	for _, waypoint in ipairs(waypoints) do
		if foundtarget == nil then
			print("DEBUG: WALKING RANDOMLY TO WAYPOINT")
		humanoid:MoveTo(waypoint.Position)
		path.Blocked:Connect(function()
			print("DEBUG: PATH BLOCKED (WALKRANDOMLY)")
			main()
		end)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		end
		if foundtarget == 1 then path:Destroy() break end
		humanoid.MoveToFinished:Wait()
		FindTarget()
	end
end
--[[
local path = game:GetService("PathfindingService"):CreatePath({
	AgentRadius = 2,
	AgentHeight = 5,
	AgentCanJump = false
})
--]]
local function findPath(target)

	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 2,
		AgentHeight = 5,
		AgentCanJump = true
	})
	if WalkStraight == false then
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	end
	local waypoints = path:GetWaypoints()

	for _, waypoint in ipairs(waypoints) do
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				path.Blocked:Connect(function()
					print("DEBUG: PATH BLOCKED (findPath")
					findPath(target)
				end)
				task.wait()
				if target == nil then
					break
				elseif target.Parent == nil then
					break
				end
			until checkSight(target) == false
			if not stunned.Value then
			humanoidrootpart:SetNetworkOwner(nil)
			end
			print("DEBUG: LOST TARGET, DESTROYING PATH (findPath)")
		--	foundtarget = nil
			break
		else
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
			end
			humanoid:MoveTo(waypoint.Position)
			local waitCheck = humanoid.MoveToFinished:Wait(1)
			if not waitCheck then
				findPath(target)
				break
			end

			if (humanoidrootpart.Position - waypoints[1].Position).Magnitude > 30 then
				print("DEBUG: FINDPATH(TARGET) (findPath)")
				
				findPath(target)
				break
			end
		end
	end
end

--[[
function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	local waypoints = path:GetWaypoints()

	for _, waypoint in ipairs(waypoints) do
		humanoid:MoveTo(waypoint.Position)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				path.Blocked:Connect(function()
					print("DEBUG: PATH BLOCKED (findPath")
				main()
				end)
				task.wait()
				if target == nil then
					break
				elseif target.Parent == nil then
					break
				end
			until checkSight(target) == false
			print("DEBUG: LOST TARGET, DESTROYING PATH (findPath)")
			foundtarget = nil
			path:Destroy()
			break
		end

		if (humanoidrootpart.Position - waypoints[1].Position).Magnitude > 30 then
			print("DEBUG: FINDPATH(TARGET) (findPath)")
			findPath(target)
			break
		end
	end
end
--]]

function MoveTo(target)
	humanoid:MoveTo(target)
end

function FindTarget()
	local dist = 20
	local target
	local players = game.Players:GetPlayers()
	for i,v in ipairs(players) do
		if (v.Character) then
			local torso = v.Character.Torso
			local human = v.Character.Humanoid
			if (humanoidrootpart.Position - torso.Position).Magnitude < dist and human.Health > 0 then
				if checkSight(torso) and human.Health ~= 0 then
					print("DEBUG: TARGET = TORSO (FindTarget)")
					print(human.Health)
					target = torso 
					
				end
			end
		end
	end
	return target
end

function checkSight(target)
	local ray = Ray.new(humanoidrootpart.Position, (target.Position - humanoidrootpart.Position).Unit * 50)
	local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent}) 
	local Player = game:GetService("Players"):GetPlayerFromCharacter(target.Parent)
	if hit then
		if hit:IsDescendantOf(target.Parent) then
			if not stunned.Value then
			humanoidrootpart:SetNetworkOwner(target)
			end
			--[[
			if not targetDebounce then
			targetDebounce = true
			FraudFoundPlayer:FireClient(Player)
			task.wait(1)
			targetDebounce = false
			end
			--]]
			--[[]]
			
			MoveTo(target.Position)
			if (humanoidrootpart.Position - target.Position).Magnitude < MaxAttackDistance then
				--print("Attack")
				local RandomAttackAnim = math.random(1,2)
				if RandomAttackAnim == 1 then
					if Attack1Anim.IsPlaying == false and Attack2Anim.IsPlaying == false and not SwingDebounce then 
					SwingDebounce = true
					Attack1Anim:Play()

					newHitbox:HitStart()
					
					local RandomSwingSound = SwingSounds[math.random(1, #SwingSounds)]
					RandomSwingSound:Play()
					
					task.wait(.75)
					SwingDebounce = false
					end
					end
					--[[
				else
					if Attack2Anim.IsPlaying == false and Attack1Anim.IsPlaying == false and not SwingDebounce then
						SwingDebounce = true
					Attack2Anim:Play()
					print("Attack2")
					if Attack1Anim.IsPlaying == true then
					newHitbox:HitStart()
					local RandomSwingSound = SwingSounds[math.random(1, #SwingSounds)]
					RandomSwingSound:Play()
					end
					task.wait(.75)
					SwingDebounce = false
					end
				end
				--]]
				WalkStraight = true
			end
			--print("DEBUG: FOUND TARGET (checkSight)")
			foundtarget = 1
			return true
		end
	else
		WalkStraight = false
		findPath(target)
	end
	return false
end


function main()
	local target = FindTarget()
	if target and foundtarget == 1 then
		print(target)
		local Player = game.Players:GetPlayerFromCharacter(target.Parent)
		humanoid.WalkSpeed = 18
		findPath(target)

	else
		foundtarget = nil
		humanoid.WalkSpeed = 7
		walkRandomly()
	end
end

humanoid.Running:Connect(function(speed)
	if speed > 17 then
		IdleAnim:Stop()
		WalkAnim:Stop()
		RunAnim:Play()
	elseif speed > 4 and speed < 17 then
		IdleAnim:Stop()
		WalkAnim:Play()
		RunAnim:Stop()
	elseif speed <= 4 then
		IdleAnim:Play()
		WalkAnim:Stop()
		RunAnim:Stop()
	end

end)

while RunService.Heartbeat:Wait() do
	main() 
end

1 Like

I don’t know much coding and how to explain, but I’m sure if there’s no obstacle, it should just convert from pathfinding to just walktopoint

1 Like

Instead of targeting the position of the player, try targeting a direction of a set magnitude. This worked for me several times.

I’d say using the PathfindingService for few studs to catch up to a player is a bit too much, even if there are no obstacles it has to go trough some calculations.

My solution to this problem would be to use Humanoid’s MoveTo function for small-distance travels, and the PathfindingService for long-distance travels.

using simply :MoveTo() will simply calculate the intersection point using y = mx + b which takes 2 seconds to do in desmos so I guess use :DistanceToPlayer() to see if your like 30 studs away and then change it to a simple intersection formula. Its a lot of complicated math compute normally so using such a simple formula like that would work well. also do not use wait() use task.wait() same thing for spawn() use task.spawn()

to everyone saying the same thing “check if its behind a wall or not, and use pathfinding/moveto depending on if player is behind wall or not” ive tried that and it isnt working. ive used print statements to try and debug it. here is the function

function checkSight(target)
	local ray = Ray.new(humanoidrootpart.Position, (target.Position - humanoidrootpart.Position).Unit * 50)
	local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent}) 
	local Player = game:GetService("Players"):GetPlayerFromCharacter(target.Parent)
	if hit then
		if hit:IsDescendantOf(target.Parent) then
			if not stunned.Value then
			humanoidrootpart:SetNetworkOwner(target)
			end
			
			humanoid:MoveTo(target.Position)
			if (humanoidrootpart.Position - target.Position).Magnitude < MaxAttackDistance then
				--print("Attack")
				local RandomAttackAnim = math.random(1,2)
				if RandomAttackAnim == 1 then
					if Attack1Anim.IsPlaying == false and Attack2Anim.IsPlaying == false and not SwingDebounce then 
					SwingDebounce = true
					Attack1Anim:Play()

					newHitbox:HitStart()
					
					local RandomSwingSound = SwingSounds[math.random(1, #SwingSounds)]
					RandomSwingSound:Play()
					
					task.wait(.75)
					print('behind wall')
					SwingDebounce = false
					end
					end
	
				--]]
				WalkStraight = true
			end
			--print("DEBUG: FOUND TARGET (checkSight)")
			print('straight path')
			foundtarget = 1
			return true
		end
	else
		WalkStraight = false
		findPath(target)
	end
	return false
end

the only thing i need right now is someone to help make a raycast that detects if the target is behind a wall or not, and then i can use if else statements to either use a moveto or pathfinding. the reason i ask for a raycast is cause im not very expericenced with raycasting

problem with this is the player could be really close but make a quick turn behind a wall and the npc would be stuck against the wall

I ended up using an entirely different pathfinding system which works out pretty well (:

1 Like

i am sorry for reviving this but do you mind sending it?

1 Like

Sorry for the bump, but I’m wondering: what system did you use?

What system did you use to do this?

1 Like

bro deserves exclusive boiler in hell for ignoring fr

2 Likes