-
What do you want to achieve? Keep it simple and clear!
I am trying to make it so that multiple NPCs are able to detect the player. I’ve made it so these NPCs get a tag from the collection service and it’s these NPCs that should be able to do this. I’ve also stored them all in one folder to make it easier. -
What is the issue?
When trying to define thenpc
variable as all the tagged NPCs in the folder (mentioned above), I either get errors where it says “attempt to index nil with .Position” at line 53, OR there are no errors but there is only one NPC that’s able to detect you instead of all of them. -
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
I have tried to write the npc variable as a table and store the NPC models in it as an array, but it throws back the position error I described above. I’ve tried just doing a simple :GetChildren() method for the variable but that also doesn’t work and gives the same error. At the moment, I just defined npc as the currently tagged npc which explains why it works only on one NPC but I don’t know what else to do.
Here’s the script. It’s a local script and is located in StarterPlayerScripts.
Please note that the rest of the script works as intended, I just need it to be able to work with multiple NPCs and that’s what I can’t figure out.
-- Get necessary services
local RunService = game:GetService("RunService")
local PlayersService = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
-- Give all Detector NPCs the detector tags so they can detect the player
for i,v in pairs (game.Workspace.Room2.DetectorNPCs:GetChildren()) do
CollectionService:AddTag(v, "DetectorNPC")
npc = v
end
-- Set NPC and character, along with their sight and range properties
local npcSight = 0.7
local npcRange = 100
-- GUI elements and reaction time
local player = PlayersService.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local head = character:WaitForChild("Head")
local detectedevent = ReplicatedStorage:WaitForChild("PlayerDetected")
local playerGui = player.PlayerGui
local DetectionBar = playerGui:WaitForChild("DetectionGUI")
local detectionFrame = DetectionBar:WaitForChild("detectionFrame")
local reactionTime = 0.8
-- Create a table to hold TweenInfo
local tweenInfo = TweenInfo.new(
0, -- Time
Enum.EasingStyle.Quad, -- EasingStyle
Enum.EasingDirection.Out, -- EasingDirection
0, -- RepeatCount (0 implies the tween will not repeat)
false, -- Reverses (should the tween reverse once reaching the endpoint)
0 -- DelayTime
)
local characterIsInFov = false
local detectionProgress = 0 -- Variable to keep track of detection progress
local hitPlayer = nil -- Move hitPlayer variable outside the if statement
RunService.Heartbeat:Connect(function(dt)
if character and npc then -- Check if the character and the NPC are not null
-- Calculate the vector between the NPC and the character
local npcToCharacter = (head.Position - npc.Head.Position).Unit
local npcLook = npc.Head.CFrame.LookVector
local dotProduct = npcToCharacter:Dot(npcLook)
if dotProduct > npcSight then
--character is in field of view
characterIsInFov = true
else
-- character is not in field of view so we do not need to take any action
characterIsInFov = false
end
-- Prepare parameters for raycasting
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {npc}
raycastParams.IgnoreWater = true
-- Perform a raycast to check if the character is in sight
local ray = workspace:Raycast(npc.PrimaryPart.Position, (character.PrimaryPart.Position - npc.PrimaryPart.Position).Unit * npcRange, raycastParams)
-- Check if the character is in range and in field of view
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange and CollectionService:HasTag(npc, "DetectorNPC") then
if ray and ray.Instance.Parent == character then
hitPlayer = PlayersService:GetPlayerFromCharacter(ray.Instance.Parent) -- This will now update the variable declared earlier
if hitPlayer then
-- Increase the detection progress
detectionProgress = math.min(detectionProgress + dotProduct * 2 * dt, 1)
end
else
-- Decrease the detection bar when not in sight
detectionProgress = math.max(detectionProgress - 0.08 * dt, 0) -- Decrease the detection progress
end
else
-- Decrease the detection bar when not in sight
detectionProgress = math.max(detectionProgress - 0.08 * dt, 0) -- Decrease the detection progress
end
-- Animate the detection bar according to detection progress
local alpha = TweenService:GetValue(detectionProgress / 1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
DetectionBar.detectionFrame.detectionBar.Size = UDim2.new(1, 0, 0, 0):Lerp(UDim2.new(1, 0, 1, 0), alpha)
DetectionBar.detectionFrame.detectionBar.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
-- Check if the detection bar is filled completely before triggering the NPC detection
if detectionProgress >= 1 then
task.wait(reactionTime)
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange and hitPlayer then -- Make sure hitPlayer is not nil
print('Player detected: ', hitPlayer.Name)
DetectionBar.detectionFrame.detectionBar.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
detectedevent:FireServer(game.Players.LocalPlayer)
end
end
end
end)
Any help would be hugely appreciated . I’m fairly new to scripting and not that good at it so please bare with me if my code has been written badly or something like that.