I am creating a fighting simulator game in Roblox, currently I am trying to make it so NPC's don't spawn in the spawn area I have set. I have done so by creating an invisible, non collidable part that detects when something touches it. It checks if the object that touched it was a humanoid and if it's a player and destroys it if it's not a player. What I'm most confused about is that it works perfectly fine, npc's don't spawn in the spawn area, however I still receive an error "Attempt to index nil with 'FindFirstChild' and my print message I have set for debugging also doesn't fire. Maybe I shouldn't pay too much attention to it since it does in fact destroy npc's that spawn in it. But for the sake of gaining knowledge (and maybe preventing bugs in the future), I'm trying to figure out why.
My first guess was it was registering an object touching it before the npc fully loaded in, meaning it tried finding a "Humanoid" part before it existed, but removed it after it found one which would explain why it's throwing an error but also still destroying the npc. I did so by creating a boolean that would wait until it found a Humanoid part and destroy it after one was found. This did not stop the error. I have also tried replacing 'FindFirstChild' with 'WaitForChild' and still the error occurred. The last thing I thought might be the issue was that I have other objects in the spawn area. I assumed maybe something else that didn't have a Humanoid property might be firing it. However this was also proven not to be the case. Down below is the script to destroy npc in spawn area, and below that is the code that spawns the npc's.
NPC Remover Script:
local part = script.Parent
part.Transparency = 1
local function onTouch(hit)
if hit.Parent:FindFirstChild("Humanoid") and not game.Players:GetPlayerFromCharacter(hit.Parent) then
hit.Parent:Destroy()
print("Destroyed")
end
end
part.Touched:Connect(onTouch)
Npc Spawn Script:
local Area1_Enemies = game.Workspace.Area1.Enemies -- Grabs enemies folder in Area 1
local noob = "Noob"
local basicSpawnTime = 0.5 -- Sets Basic Type Npc Spawn Rate
while true do
local randZ = math.random(-744, -473)
local noobCount = 0
for _, v in pairs(Area1_Enemies:GetChildren()) do -- Counts every enemey named Noob in Area1 Enemies folder
if v.Name == noob then
noobCount += 1
end
end
if noobCount < 50 then -- If Area 1 Folder has less then 5 Noobs, spawn more
local noobClone = game.ServerStorage.Enemies.Area1.Noob:Clone()
noobClone.Parent = game.Workspace.Area1.Enemies
noobClone.HumanoidRootPart.CFrame = CFrame.new(math.random(-61, 273), 6, randZ)
end
task.wait(basicSpawnTime) -- Time before npc respawns
end
The error attempt to index nil with X
is telling you that you are trying to do something with an object that no longer exists. And I believe that you might have a race condition in the code that destroys the NPC.
It's a common problem that a character model will cause the Touched
event to fire multiple times, because you might have both feet touching a block, or the torso and the hands. So here's the series of events that I think is happening :
onTouch
handler detects that this is an NPC and Destroy()
's the part's Parent, effectively removing the whole model.nil
and starts cleaning up references to the part.onTouch
handler tries to execute.hit
has been destroyed by the previous call, hit.Parent
is nil, and you call hit.Parent:FindFirstChild
which trips the error because you are trying to call a function on a nil object.So since you are deleting objects, it might make sense to do some sanity checks and escape if an object has been queued for deletion.
local function onTouch(hit)
-- escape if this object has already been deleted
if hit == nil or hit.Parent == nil then
return
end
local hasHumanoid = hit.Parent:FindFirstChild("Humanoid")
local isNotPlayer = not game.Players:GetPlayerFromCharacter(hit.Parent)
if hasHumanoid and isNotPlayer then
hit.Parent:Destroy()
print("Destroyed")
end
end