Problem with player detection system

So I’m currently making a detection system but I got a problem that it doesn’t also detect more then one player so it will always already stop if it got the player but will for an example not see the dead body (also shown in the video) and I don’t know how to fix it:

This is the code:

--||Services||--
local PlayerService = game:GetService("Players")
local CollectionService = game:GetService("CollectionService")
local SoundService = game:GetService("SoundService")
local Debris = game:GetService("Debris")
local TS = game:GetService("TweenService") 
local RS = game:GetService("ReplicatedStorage")

--||Table||--
local npcModels = workspace.Npcs:GetChildren()

--||Settings||--
local fieldOfView = 80 -- The NPC's field of view in degrees
local awarenessDecrease = 15  -- Adjust the rate at which awareness decreases
local awarenessSpeed = 75 --if you increase this the enemy gets aware quicker

--||Value||--
local objectLastDetected = {} -- The object the NPC last saw

--||Animations||--
local Looking_For_Idle_Animation = RS.Animations.Npc.Looking_For_Idle
local Found_Idle_Animation = RS.Animations.Npc.Found_Idle
local Found_Start_Animation = RS.Animations.Npc.Found_Start
local Searching_For_Idle_Animation = RS.Animations.Npc.Searching_For_Idle

local Looking_For_Idle_Anim = {}
local Found_Idle_Anim = {}
local Found_Start_Anim = {}
local Searching_For_Idle_Anim = {}

-- Initialize table to keep track of animations for each NPC
local isAnimationPlaying = {}

-- Function to check if an animation is playing for an NPC
local function animationIsPlaying(npc)
	return isAnimationPlaying[npc] == true
end

------------------------------------------------------------------------------------------------------------------



local function getCharacters()
	local characters = {}
	local players = PlayerService:GetPlayers()

	for index, player in pairs(players) do
		table.insert(characters, player.Character)
	end
	
	for index, npc in pairs(CollectionService:GetTagged("Ragdolled")) do --check for ragdolled tag		
		table.insert(characters, npc)
	end

	return characters
end

local function raycast(npc, start, finish)
	local parameters = RaycastParams.new()

	parameters.FilterDescendantsInstances = {npc, getCharacters()}
	parameters.FilterType = Enum.RaycastFilterType.Exclude

	local raycast = workspace:Raycast(start, (finish - start), parameters)

	if raycast then
		return true
	else
		return false
	end
end

local function checkSight(npc)
	local detectable = {}

	local characters = getCharacters()

	for index, character in pairs(characters) do
		table.insert(detectable, character)
	end
	
	for index, object in pairs(detectable) do
		task.spawn(function() --this task spawn function destroys the detection system
			if object:IsA("Model") then
				object = object.PrimaryPart
			end	

			local headPosition = npc.Head.Position

			local objectPosition = object.Position
			local headCFrame = npc.Head.CFrame

			local npcToObject = (objectPosition - headPosition).Unit
			local npcLookVector = headCFrame.LookVector

			local dotProduct = npcToObject:Dot(npcLookVector)
			local angle = math.deg(math.acos(dotProduct))

			local distance = (headPosition - objectPosition).Magnitude

			if angle > fieldOfView then
				return
			end     

			if distance > npc:GetAttribute("DetectionRange") then
				return
			end
			
			if raycast(npc, headPosition, objectPosition) then
				return
			end

			if object:IsA("Model") then
				objectLastDetected[npc] = object.Parent
			else
				objectLastDetected[npc] = object
			end
			
			print(object.Parent)

			return object.Parent
		end)
	end
end

for index, npc in pairs(npcModels) do
	local humanoid = npc:FindFirstChildOfClass("Humanoid")
	local hrp = npc.PrimaryPart

	if not npc:IsA("Model") then
		continue
	end

	if not humanoid then
		continue
	end
	
	-- Get the original pos and cframe
	local currenCFrame = {}
	local currenPos = {}
	currenCFrame[npc] = npc.HumanoidRootPart.CFrame
	currenPos[npc] = npc.HumanoidRootPart.Position

	-- Load animations outside the loop
	Found_Idle_Anim[npc] = humanoid.Animator:LoadAnimation(Found_Idle_Animation)
	Found_Start_Anim[npc] = humanoid.Animator:LoadAnimation(Found_Start_Animation)
	Looking_For_Idle_Anim[npc] = humanoid.Animator:LoadAnimation(Looking_For_Idle_Animation)
	Searching_For_Idle_Anim[npc] = humanoid.Animator:LoadAnimation(Searching_For_Idle_Animation)

	isAnimationPlaying[npc] = false

	local function checking()
		while task.wait(.5) do
			if not npc:GetAttribute("Stunned") and not npc:GetAttribute("IsRagdoll") then
				local object = checkSight(npc)
				print(object)
				if object and humanoid.Health > 0 and not object:GetAttribute("Hidden") then                
					-- Code for when an NPC sees a detectable target                
					local objectHRP = object.HumanoidRootPart

					local distance = math.floor((objectHRP.Position - hrp.Position).Magnitude + 0.5)
					--print("is aware of "..object.Name)

					local awarenessIncrease = awarenessSpeed - distance 

					if awarenessIncrease < 0 then awarenessIncrease = 0 end    

					TS:Create(hrp, TweenInfo.new(.3), {CFrame = CFrame.new(hrp.Position, Vector3.new(objectHRP.Position.x,hrp.Position.Y,objectHRP.Position.z))}):Play() --tweening the to look at the object

					-- Check if awareness reaches 100
					if npc:GetAttribute("Awareness") >= 100 then
						npc:SetAttribute("Target", object.Name)
						npc:SetAttribute("Awareness", 100)
						--print("found "..object.Name.. " their distance is "..distance.. " studs")

						-- Stop other animations and play found animation
						if not animationIsPlaying(npc) then
							Looking_For_Idle_Anim[npc]:Stop() 
							Searching_For_Idle_Anim[npc]:Stop() 

							Found_Start_Anim[npc]:Play()
							task.delay(.3,function()
								Found_Idle_Anim[npc]:Play()
							end)				
							isAnimationPlaying[npc] = true
						end

						local FoundSound = SoundService.SFX.Npc.Found:Clone()
						FoundSound.Parent = hrp
						FoundSound:Play()
						Debris:AddItem(FoundSound,2)

						npc.Head.Awareness.Frame.Yellow.Visible = false
						npc.Head.Awareness.Frame.Red.Visible = true
					elseif npc:GetAttribute("Awareness") < 100 then
						--we only do this when the awareness is under 100
						npc:SetAttribute("Awareness", npc:GetAttribute("Awareness") + awarenessIncrease)

						-- Stop found animation if it's playing
						if animationIsPlaying(npc) then
							Found_Start_Anim[npc]:Stop()
							Found_Idle_Anim[npc]:Stop()
						end

						-- Play looking for animation if not already playing
						if not animationIsPlaying(npc) then
							Looking_For_Idle_Anim[npc]:Play()
							isAnimationPlaying[npc] = true
						end

						if npc:GetAttribute("Awareness") >= 100 then
							isAnimationPlaying[npc] = false
						end

						local AwareSound = SoundService.SFX.Npc.Aware:Clone()
						AwareSound.Parent = hrp
						AwareSound:Play()
						Debris:AddItem(AwareSound,2)

						npc.Head.Awareness.Frame.Yellow.Visible = true
						npc.Head.Awareness.Frame.Red.Visible = false
					end
				elseif not object and npc:GetAttribute("Awareness") > 0 then            
					-- Code for when an NPC does not see a detectable object
					npc:SetAttribute("Target", "-")
					npc:SetAttribute("Awareness", npc:GetAttribute("Awareness") - awarenessDecrease)

					-- Stop found animation if it's playing
					if Found_Idle_Anim[npc].IsPlaying == true or Found_Start_Anim[npc].IsPlaying == true or Looking_For_Idle_Anim[npc].IsPlaying == true then
						Found_Idle_Anim[npc]:Stop()
						Found_Start_Anim[npc]:Stop()
						Looking_For_Idle_Anim[npc]:Stop()
						isAnimationPlaying[npc] = false
					end

					-- Play searching for animation if awareness is still positive and it's not already playing
					if npc:GetAttribute("Awareness") > 0 and not animationIsPlaying(npc) then
						Searching_For_Idle_Anim[npc]:Play() 
						isAnimationPlaying[npc] = true
					end

					local AwareSound = SoundService.SFX.Npc.Aware:Clone()
					AwareSound.Parent = hrp
					AwareSound:Play()
					Debris:AddItem(AwareSound,2)

					npc.Head.Awareness.Frame.Yellow.Visible = true
					npc.Head.Awareness.Frame.Red.Visible = false

					-- Ensure awareness doesn't go below 0
					if npc:GetAttribute("Awareness") <= 0 then
						npc:SetAttribute("Awareness", 0)
						npc.Head.Awareness.Frame.Yellow.Visible = false
						npc.Head.Awareness.Frame.Red.Visible = false
						Found_Idle_Anim[npc]:Stop()
						Found_Idle_Anim[npc]:Stop()
						Looking_For_Idle_Anim[npc]:Stop() 
						Searching_For_Idle_Anim[npc]:Stop()
						isAnimationPlaying[npc] = false

						npc.Humanoid:MoveTo(currenPos[npc])
						npc.Humanoid.MoveToFinished:Wait()

						task.wait(1)

						if npc:GetAttribute("Stunned") or npc:GetAttribute("Awareness") > 0 then npc.Head.Awareness.Frame.Yellow.Visible = false return end

						if npc:GetAttribute("Target") == "-" then
							npc.Head.Awareness.Frame.Yellow.Visible = false
							TS:Create(npc.HumanoidRootPart, TweenInfo.new(.5), {CFrame = currenCFrame[npc]}):Play() 
						end

					end
				end
			end		
		end
	end

	coroutine.wrap(checking)()
end


At the first print line in the checkSight function it gives me correctly the character but then in the checking function it prints me nil wich confuses me.

The thing is that it works normally without that task.spawn function in the checkSight function but the other problem then is that it will always only get the player and not also a ragdolled enemy.

Video:

Thanks in advance and tell me if you need more info.

Could you edit your topic to be more concise? For example, “Help with my object/player detection system” because just putting “detection system” could mean a lot of different things like “Exploit detection” or “Health detection,” if you know what I mean.

Also please explain what you’re trying to achieve
Thanks :slight_smile:

It looks like task.spawn might be your issue
I think…

task.spawn is asynchonous, which means that it executes the code in a new thread, and could lead to timing issues where checkSight does not return the expected results in time, because of the task.spawn

This is a lot of code and I can’t say I looked at it really in depth. I would explore this first, or try to narrow down the code that you give to the forum.

1 Like

my adhd just wont let me sit and analyze the code haha

1 Like

I already said that myself, did you even read what I said :confused:

What I’m trying to achieve is shown in the video

Could you maybe explain it here? In the video you only demonstrated and showed the output which looks kind of confusing to me. it’d help a lot if you explain clearly what is your expected result and what you’re trying to do (Detecting if the player is in the sphere zone?)

as you can see at around 40 seconds in the video the enemy detects the dead body and the player but doesn’t get triggered by that, but then if I remove the task.spawn function you can see at 1:08 in the video that it doesn’t detect the dead body anymore and only can detect the player (1:17)