this.transform.FindChild(x) and GameObject.Find(x) returning (apparently?) different results

Hi everyone,

So I’m a long time lurker (my first post!), and these forums are a great resource, but this problem is puzzling me. Let me put you in context:

  • I have 2 scenes (Game and Upgrade)
  • Between these 2 scenes, the Player gameObject travels as a singleton, carrying with him a bunch of children, among which weapons. It goes like: Player/WeaponFamily/WeaponName
  • In the Upgrade scene, I have a button that sets a Weapon to an “equipped” status. Upon re-entering the Game scene, I check which weapon has the “equipped” status true and assign it to the Player’s weapon variable.

Here is the code in the Game scene that checks which weapon is equipped and assigns it to the player:

// Find which weapon is equipped and actually equip it
Weapon[] weapons = transform.GetComponentsInChildren<Weapon> ();    
foreach (Weapon w in weapons)
{
    if (w.IsEquipped()) {weapon = w; break;}
}

This finds that all weapons have the “equipped” flag set to False. However, the editor clearly shows me that I do have a weapon with an “equipped” status to true:

This is what I see in the editor upon exit of the Upgrade scene: it means my changes there were saved to that specific Weapon’s script.

However, and this is what I don’t get, if I use this code:

Weapon[] weapons = GameObject.Find ("Gun").transform.GetComponentsInChildren<Weapon> ();
foreach (Weapon w in weapons)
{
	if (w.IsEquipped()) {weapon = w; break;}
}

It works!

I have thought that maybe I wasn’t removing properly the Children for the singleton Player gameObject, but I tried to specifically remove them all and still the same error happens (that “weapon” is null).

Obviously I could go on using the working code, but I can’t see the difference between the 2 and I don’t get why one works and the other no, so if anyone knows what I’m doing wrong, he’d be very welcome :slight_smile:

Thanks for reading and sorry for the long post!

Edit: for list formatting and title

It should be fine, then. And the variable should be private - no other class should ever need to access it.

Aaaand, I think I found the issue. You’re saying that you’re assigning the weapon as equipped when you enter the scene, right? Are you doing this in Start()?

So, your singleton is set to DontDestroyOnLoad. This means that it stays alive between scenes. In particular, this also means that Start isn’t called again when you change scenes.

So, what’s happening is that the Start method being called is the Start method on your other Player object (the one being destroyed). That is where the equipment behaviour is being started. When you do this:

if (!hasBeenCreated)
{
    ...
}
else
{
    GameObject.Destroy (this.gameObject);
}

//More Start Stuff!

GameObject.Destroy doesn’t stop the control flow - so everything later in your Start() method (“More Start Stuff”) is being executed by the to-be-destroyed script. This is why GameObject.Find makes your code work, as it finds the singleton instead of the local, to-be-destroyed object.

your singleton’s Start should look like this:

else {
    GameObject.Destroy(this.gameObject); // or just Destroy(gameObject)
    return; //STOP execution of Start!
}

After you’ve fixed that, you’ll have to take all the code that’s supposed to be called every time a level is loaded, and move it to a method. This method needs to be called both on Start (AFTER destroying other singletons), AND in the special OnLevelWasLoaded method, which is called whenever a new level is loaded after that. The OnLevelWasLoaded method takes an int argument that specifies the level that was loaded, so you can avoid calling the level start stuff on the upgrade level.