How to :FindFirstChild():FindFirstChild()?

this may sound a little dumb but are you able to findfirst after a findfirst? since i dont want my script to be

if NPC:FindFirstChild("Visuals") then
	if NPC.Visuals:FindFirstChild("Particle") then
		-- BLAH BLAH BLAH CODE HERE
	end
elseif NPC.Parent:FindFirstChild("Visuals") then
	if NPC.Parent.Visuals:FindFirstChild("Particle") then
		-- BLAH BLAH BLAH CODE HERE
	end
end

that isnt the actual code but just a example of what im trying to say

As long as the object exists? Yes
Is it recommended? No

1 Like

well idk if you understand you cant exactly do a :findfirstchild():findfirstchild() i wanted to know if there was a better way to do it other than the way i just showed

You could, but its not going to do much:

local Visuals = NPC:FindFirstChild("Visuals") -- returns nil if no Visual
local Particle = Visuals and Visuals:FindFirstChild("Particle") -- returns nil if no Visual or no Particle

if Particle then
  -- this and that
end

The problem here is that

  1. You have two seperate locations for possibly the exact same result
    Keep it consisten for NPC’s of the same class.
  2. If you are going to reuse an object, use a variable.
    A variable will save time, and remove a bunch of unessecary code

  1. Rude
  2. You can, and as mentioned:
1 Like

ok, im sorry i stated this wrong lol heres the error
image

i meant is is there a way to do findfirstchild:findfirstchild.Value? heres a example i have

local function Rope()
	local result, mousePosition = MouseRaycast()

	if result and result.Instance and mousePosition then

		local x = result.Position.X
		local y = result.Position.Y
		local z = result.Position.Z

		local tp2 = game.Workspace.Visuals.RopeEnd2

	if result.Instance.Parent.Parent and result.Instance.Parent.Parent.Name == "NPCS" or result.Instance.Parent.Parent.Parent and result.Instance.Parent.Parent.Parent.Name == "NPCS" then
			if result.Instance.Parent:FindFirstChild("Info"):FindFirstChild("LineValue").Value == 1 or result.Instance.Parent.Parent:FindFirstChild("Info"):FindFirstChild("LineValue").Value == 1 then
				print("Found LineValue")
			end
		end
	end
end

Your error is happening because Info doesn’t exist. I suggest restructuring your code or the way you are parenting your objects because it seems to be getting pretty messy. Regardless, you can fix it:

local info1 = result.Instance.Parent:FindFirstChild("Info")
local info2 = result.Instance.Parent.Parent:FindFirstChild("Info")

if info1 then
   local lineValue = info1:FindFirstChild("LineValue") 
   if lineValue and lineValue.Value == 1 then 
      print("Found LineValue")
   end
elseif info2 then
   local lineValue = info2:FindFirstChild("LineValue") 
   if lineValue and lineValue.Value == 1 then 
      print("Found LineValue")
   end
end

How about FindFirstChildWhichIsA?

1 Like

ok i have done this,. but it works but it doesnt, its super weird the value is 1, i print it and it says 1 but when i check if its 1 it returns false

sorry i have fixed it, it was a string value and not a number value :smiley:

hey there, just so you know, YES you can actually use findfirstchild after using it again, keep in mind that if you’d like to minimize errors with the script saying its null or something then you can use :WaitForChild(), but if you’re willing to use that i’m 90% sure they don’t work in if [code] then end statements.

if your code does not require immediate execution, you could use wait(game:IsLoaded()), and then wait() for a few seconds. what i’m saying IS unrelated but if the object youre trying to find is far away from the spawn area or rendering distance of the player you could just use repeat wait() until NPC.Parent.Visuals:FindFirstChild("Particle")

i’m not really updated on the discussion so that’s all i can say without more context

It is possible to chain Instance:FindFirstChild calls. However, that is not how the function is meant to be used. The function is used to access children of an instance that you’re unsure exists, or are named after a property/function of the parent instance. Because it has the capacity to return nil, it is unsafe to chain this function. A better term to describe what you want is called optional chaining, and there are languages with direct support for this. One of these languages is TypeScript, and there exists a transpiler to Luau with support for the Roblox Studio API:

const child = npc.FindFirstChild("Visuals")?.FindFirstChild("Particle");

You can implement this as a monad in Luau:

local function Maybe(value)
    local monad = {}
    local result = value
        
    function monad.advance(transform, ...)
        if result then
            result = transform(result, ...)
        end
        
        return monad
    end
    
    function monad.result()
        return result
    end
    
    return monad
end
local particles = Maybe(npc).advance(npc.FindFirstChild, "Visuals")
                            .advance(npc.FindFirstChild, "Particle")
                            .result()

But this specific utilization is taking advantage of language properties that are not commonly abused, and therefore may be difficult to understand.

If you know no second descendant of the NPC could have the name “Particles”, then you could use the recursive mode of FindFirstChild:

local particles = npc:FindFirstChild("Particles", true)
-- See API reference on second argument

If your main concern is code readability, you can reduce indentation using techniques from the never-nesting principle, which include extraction, and what’s most useful in your case, inversion. Inversion inverts conditionals and sets points of early exits:

local visuals = npc:FindFirstChild("Visuals")
if visuals then
    local particles = npc:FindFirstChild("Particles")
    if particles then
        -- ...
    end
end

Is converted to:

local visuals = npc:FindFirstChild("Visuals")
if not visuals then
    return
end

local particles = npc:FindFirstChild("Particles")
if not particles then
    return
end

-- ...

Since you’ll need to hold onto the return results of the previous calls, you can use another language feature called short-circuiting to remove the leading if-statements altogether. This was demonstrated by @DasKairo in this reply

3 Likes

wow. thanks for all that and for going into detail im definently going to have to try something like this in some other projects!

Instance:WaitForChild can be used in any conditional. In OP’s case, this isn’t a matter about instances not existing at the desired time, but about the code’s ability to interact with instances that may not be desired. This is what leads to an structural insufficiencies. On that note

  1. wait is deprecated.
  2. game:IsLoaded does not return a number, so you’ll be waiting 1/30th of a second instead of for the game to load.
  3. Instance streaming would render the model’s contents missing beyond the “Particles” child of npc.Visuals, leading to other indexing errors

@coolchristopher912_2, you should put more trust in the instances your raycasts are picking up. Instead of over-analyzing their structure, use the filtration component of the raycasting API to eliminate undesirable instances from the get-go

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.