Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question GameObject not always found

Discussion in 'Scripting' started by AquaBall60_, Jul 2, 2023.

  1. AquaBall60_

    AquaBall60_

    Joined:
    May 13, 2023
    Posts:
    12
    I am experiencing some strange behavior:
    Some of my GameObjects (about 10-20 elements) are not always found!?
    At the first run everything works correctly, but on the 2nd run some(!) are not found.
    Try/catch shows: Search result find(...) => null.
    (The 2nd pass is a kind of second level, but same stage, same static objects, ...)
    In my gObj hierarchy all elements definitely exist correctly!


    Probably nobody can help me concretely at this complex constellation, but you might help me debugging:
    A) How can I debug further WHY the elements are not found?
    B) How can I check/print a log of the existing elements at this very moment?
    C) Is there another way, to get the objects?
    Code (CSharp):
    1.     gObj = GameObject.Find(ID);
    2.     infoText = GameObject.Find($"{ID}Info");
    3.     infoText2 = GameObject.Find($"{ID}Info2");
    (The {ID}-value ist correct, and objects do exist!)

    A (shrinked) log shows, that mostly TMP, but even plain GameObject are involved:

    suche 'HLInfo' failed!!
    suche 'HLInfo2' failed!!
    suche gObj 'HR' ok
    suche 'HRInfo' failed!!
    suche 'HRInfo2' failed!!
    suche gObj 'HB' ok
    suche 'HBInfo' failed!!
    suche 'HBInfo2' failed!!
    suche gObj 'FL' failed!!
    suche 'FLInfo' failed!!
    suche 'FLInfo2' failed!!

    And again: on First run ALL Objects are found correctly.
     

    Attached Files:

  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    GameObject.Find is actually considered bad practice. It will fail if you rename an object in the scene, and it will fail searching for the name of a prefab because the instance may have „(1)“ suffix and so on.

    You can drag and drop references in the Inspector to fields so you don‘t have to „find“ them. You can also put all objects belonging together as children of another object, and through that parent object you can enumerate all children:

    foreach (Transform child in transform) …
     
    Bunny83 likes this.
  3. AquaBall60_

    AquaBall60_

    Joined:
    May 13, 2023
    Posts:
    12
    Sure, but neither is the case!

    I know, but have to do it by Code, depending on Object+ID.

    That's the hint. but:
    REALLY?!?!
    It could all be so simple!
    => return GameObject.Find(id);
    But no!!! Now I have to:
    actually search manually? (3 levels of hierarchy)
    actually create variables + assign in inspector
    actually recopy from static? ( 3 + 3 variables, plus extra code)
    static data field can't be assigned in inspector / nonStatic can't be used in class.

    This means I have to use this monster, and need 6 plus variabes!!!
    Code (CSharp):
    1.         GameObject findID(string id)
    2.         {
    3.             // Es könnte alles so einfach sein!
    4.             //return GameObject.Find(id);
    5.  
    6.             foreach (Transform p in statKörper)
    7.                 foreach (Transform k in p)
    8.                     if (k.name.Equals(id)) return k.gameObject;
    9.             foreach (Transform k in statInfo1)
    10.                 if (k.name.Equals(id)) return k.gameObject;
    11.             foreach (Transform k in statInfo2)
    12.                 if (k.name.Equals(id)) return k.gameObject;
    13.             return null;
    14.         }
    At least it workes now!
    Thx for your valuable hint!
     
    Last edited: Jul 2, 2023
  4. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    521
    Is there no way you could add the GameObjects to a Dictionary using the ID for the key?
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    Remember the first rule of GameObject.Find():

    Do not use GameObject.Find();

    More information: https://starmanta.gitbooks.io/unitytipsredux/content/first-question.html

    More information: https://forum.unity.com/threads/why-cant-i-find-the-other-objects.1360192/#post-8581066

    In general, DO NOT use Find-like or GetComponent/AddComponent-like methods unless there truly is no other way, eg, dynamic runtime discovery of arbitrary objects. These mechanisms are for extremely-advanced use ONLY.

    If something is built into your scene or prefab, make a script and drag the reference(s) in. That will let you experience the highest rate of The Unity Way(tm) success of accessing things in your game.


    Keep in mind that using GetComponent<T>() and its kin (in Children, in Parent, plural, etc) to try and tease out Components at runtime is definitely deep into super-duper-uber-crazy-Ninja advanced stuff.

    This sort of coding is to be avoided at all costs unless you know exactly what you are doing.

    If you run into an issue with any of these calls, start with the documentation to understand why.

    There is a clear set of extremely-well-defined conditions required for each of these calls to work, as well as definitions of what will and will not be returned.

    In the case of collections of Components, the order will NEVER be guaranteed, even if you happen to notice it is always in a particular order on your machine.

    It is ALWAYS better to go The Unity Way(tm) and make dedicated public fields and drag in the references you want.
     
  6. AquaBall60_

    AquaBall60_

    Joined:
    May 13, 2023
    Posts:
    12
    How did I get a punch of GameObject into a dictionary.
    (without declaring all variables, assigning them in inspector, adding each line single, refactoring the search, ...)?
    The Objects are now 12 so far, but might be 80 in final stage!
     
  7. AquaBall60_

    AquaBall60_

    Joined:
    May 13, 2023
    Posts:
    12
    I'll read the links!

    I think exactly that's what I need: dynamic runtime discovery
    Maybe I'm wrong!
    Thx
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    Then use it, but understand how it can fail. It can fail in many ways such as not spelling something precisely.

    It is not possible for us to magically understand how it has failed you. YOU must debug it.

    If you just want to inspect EVERY GameObject in your scene, then use
    FindObjectsOfType<Transform>()
    and iterate the returned collection.

    As you iterate Transforms from the scene, you could use GetComponent<T>() to see if they have a Component that you are interested in, or you could check the name yourself.

    Otherwise...

    Time to start debugging! Here is how you can begin your exciting new debugging adventures:

    You must find a way to get the information you need in order to reason about what the problem is.

    Once you understand what the problem is, you may begin to reason about a solution to the problem.

    What is often happening in these cases is one of the following:

    - the code you think is executing is not actually executing at all
    - the code is executing far EARLIER or LATER than you think
    - the code is executing far LESS OFTEN than you think
    - the code is executing far MORE OFTEN than you think
    - the code is executing on another GameObject than you think it is
    - you're getting an error or warning and you haven't noticed it in the console window

    To help gain more insight into your problem, I recommend liberally sprinkling
    Debug.Log()
    statements through your code to display information in realtime.

    Doing this should help you answer these types of questions:

    - is this code even running? which parts are running? how often does it run? what order does it run in?
    - what are the names of the GameObjects or Components involved?
    - what are the values of the variables involved? Are they initialized? Are the values reasonable?
    - are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

    Knowing this information will help you reason about the behavior you are seeing.

    You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as
    Debug.Log("Problem!",this);


    If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

    You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

    You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

    You could also just display various important quantities in UI Text elements to watch them change as you play the game.

    Visit Google for how to see console output from builds. If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer for iOS: https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/ or this answer for Android: https://forum.unity.com/threads/how-to-capturing-device-logs-on-android.528680/

    If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

    Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

    Here's an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

    https://forum.unity.com/threads/coroutine-missing-hint-and-error.1103197/#post-7100494

    "When in doubt, print it out!(tm)" - Kurt Dekker (and many others)

    Note: the
    print()
    function is an alias for Debug.Log() provided by the MonoBehaviour class.
     
  9. AquaBall60_

    AquaBall60_

    Joined:
    May 13, 2023
    Posts:
    12
    @Kurt-Dekker:
    Thanks for the detailed help!
    I will read in more detail next weekend. (Especially since it now runs correctly. The rest is code optimization, and "nicer" programming).

    Back to the original behavior:
    In the first level all objects are found. In the 2nd only partially. Maybe really a timing problem.
    In any case, the type/name and existence of the objects are correct and are not changed.


    (Besides Debug.log I use a lot of breakpoints in VisualStudio).

    Thanks for the help, there are a lot of tips in there.
     
    Kurt-Dekker likes this.
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    That's a good theory for sure. Timing can be tricky. Check this out:

    Here is some timing diagram help:

    https://docs.unity3d.com/Manual/ExecutionOrder.html

    Here is an abbreviated and better-annotated version of the above:

    https://forum.unity.com/threads/a-c...ution-order-of-unity-event-functions.1381647/

    Two good discussions on Update() vs FixedUpdate() timing:

    https://jacksondunstan.com/articles/4824

    https://johnaustin.io/articles/2019/fix-your-unity-timestep
     
  11. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,503
    This is a perfectly reasonable thing to need. However, keep in mind that there are almost always better ways to specify what you're searching for than by its name, and if there are not, then there is a good chance that you are (or someone else is) making life much harder than it needs to be.

    Do you know nothing more that would help you get the right object? Can you not equip yourself better as the person writing this system?

    As one example, lets say I'm making a farm sim and I need a list of all plants. The player makes the plants, so I can't make the list at edit time. In that context...
    ...if my plants all have a Plant component, then that component could add them to a list in Start or OnEnable, and remove them in OnDestroy or OnDisable. That list could be in a "Manager" object somewhere, for instance.

    If they have a Plant component, this can also be used for GetComponentsInChildren and/or FindObjectsOfType.

    It could also be worth looking into "events" and the various ways they could be applied here. These are nice because the plants don't have to know or care about what happens with the information they transmit.

    (Some approaches don't require a component or even a GameObject for the plant at all, but they're not relevant to the question.)
     
    Bunny83, spiney199 and Kurt-Dekker like this.
  12. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    Most people do not use GameObject.Find and therefore often do not know / care about its limitation. Though the documentation does mention it.

    Though considering the naming of your objects, it seems you currently use a group of 3 objects which belong to a certain "ID" but they are completely unstructured just next to each other... If those 3 objects belong together, why not simply creating an "HP" gameobject and have it two children named "Info" and "Info2"? Do that for every of your instances. Like others have already mentioned, you can setup references in the inspector. So giving each parent object a script that has two Transform variables, each for one of the children, you can simply link those children in the inspector and access them at any time through those variables from the script on the parent.

    Unity also supports "prefabs". So you can turn one of these instances into a prefab and instantiate it several times and all children would be cloned as well and the links to its children would also work as expected.

    You haven't given much information what this is about, how those objects are created in the first place and what they actually represent. This leaves us just fishing in the dark. Having a List of your main objects / the script on the main object would let you either drag and drop the objects as you author them inside the Unity editor, or if they are somehow dynamically created at runtime, you can add them manually through code when they are created. There's even a third option to simply use FindObjectsOfType to find all instances inside Start once and then just store them in the list or dictionary or whatever fits the situation. There are countless possibilities and GameObject.Find is the worst for performance and also limits its use since it can not find deactivated objects.

    Using GameObject.Find is like using the PA system of a shopping mall when you want to talk to your child. Put it where you can find it makes it much easier ^^. And the great thing about gameobjects, unlike children they don't just run away. Though when you send them "sleeping" they won't hear the call :)