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

Is it better to find a GameObject with a tag or to just assign it in the inspector? (Public gObject)

Discussion in 'Scripting' started by Rutenis, Apr 22, 2016.

  1. Rutenis

    Rutenis

    Joined:
    Aug 7, 2013
    Posts:
    297
    Hey, so today i was wondering if its better to find a specific GameObject with a tag in code or just make it a public and assign it in the inspector. Which way is better and more efficient or even is it the same?



    -Thanks!:)
    -Cheers!
     
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    "better" in what way :p

    assigned in the inspector doesn't need to do anything to work out the reference, so it's probably more "efficient" by the smallest tiny fraction. Draw back there is that you have to manually assign it, so it has to be known at build time...
     
    Rutenis likes this.
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,181
    Getting by tag has a big drawback in that the code will find the first object it can find with that tag. Did you put some other object with the same tag in the scene? BAM, your code broke.

    When you assign in the inspector, you know exactly what object's assigned.

    It's also a lot faster to assign in the inspector - FindByTag needs to search through all of the objects in your scene! The difference will only be noticeable if you're finding by tag in every Update call, though.
     
    Kiwasi and Rutenis like this.
  4. FuguFirecracker

    FuguFirecracker

    Joined:
    Sep 20, 2011
    Posts:
    419
    EDIT#
    Retracted.
    Baste was right
    LOL
     
    Last edited: Apr 25, 2016
  5. The-Little-Guy

    The-Little-Guy

    Joined:
    Aug 1, 2012
    Posts:
    297
    I did a test a few days ago, where I had 5,000 gameObjects, that would automatically load themselves into a static List using OnEnable. (Note: The following blocks of code are written from memory, as the real code is on my other computer). It looked something like this:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. public class MyObject : MonoBehaviour {
    5.  
    6.     void OnEnable(){
    7.         MyClass.Register(this);
    8.     }
    9.  
    10. }
    It then loaded into this static class:

    Code (CSharp):
    1. using System.Collections.Generic;
    2.  
    3. public class MyClass {
    4.  
    5.     private static List<MyObject> items = new List<MyObject>();
    6.  
    7.     public static GameObject[] GetByTag(string tag){
    8.         return (
    9.             from i in items
    10.             where i.gameObject.tag == tag
    11.             select i.gameObject
    12.         ).ToArray();
    13.     }
    14.  
    15.     public static void Register(MyObject obj){
    16.         if(!items.Contains(obj)){
    17.             items.Add(obj);
    18.         }
    19.     }
    20.  
    21. }
    I then benchmarked this against GameObject.FindGameObjectsWithTag(), to see which one would return results faster, and here was my findings (Time in seconds):

    Code (CSharp):
    1. FindGameObjectsWithTag: 0.0002
    2. Linq GetByTag:          0.0022
    Using this:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class TestSpeed : MonoBehaviour {
    4.  
    5.     void Start(){
    6.         float start1 = Time.realtimeSinceStartup;
    7.         MyClass.GetByTag("My Game Object");
    8.         Debug.Log(Time.realtimeSinceStartup - start1);
    9.      
    10.      
    11.         float start2 = Time.realtimeSinceStartup;
    12.         GameObject.FindGameObjectsWithTag("My Game Object");
    13.         Debug.Log(Time.realtimeSinceStartup - start2);
    14.     }
    15.  
    16. }
    Maybe it was the linq that made this really slow, or maybe FindGameObjectsWithTag is faster than you would expect... Maybe I am just doing something that is really wrong....
     
    Last edited: Apr 24, 2016
    Rutenis likes this.
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,181
    It's probably because the Linq implementation in the old-ass Mono Unity's using is really bad.

    I've done this test before, but here's a new example:

    Code (csharp):
    1.  
    2.     private IEnumerator Start() {
    3.         //wait one second between each action to be "sure" that Unity's not doing anything else
    4.         yield return new WaitForSeconds(1f);
    5.  
    6.         //create 10k objects with the tag "1"
    7.         for (int i = 0; i < 10000; i++) {
    8.             (new GameObject("Tag 1 " + i)).tag = "1";
    9.         }
    10.  
    11.         yield return new WaitForSeconds(1f);
    12.  
    13.         //Check how long it takes to find all objects with that tag. The result on my computer is ~0.0008 seconds
    14.         float time = Time.realtimeSinceStartup;
    15.         GameObject.FindGameObjectsWithTag("1");
    16.         Debug.Log(Time.realtimeSinceStartup - time);
    17.  
    18.         yield return new WaitForSeconds(1f);
    19.  
    20.         //Create 10k more objects with the tag "1"
    21.         for (int i = 0; i < 10000; i++) {
    22.             (new GameObject("Tag 1" + i + 10000)).tag = "1";
    23.         }
    24.  
    25.         yield return new WaitForSeconds(1f);
    26.  
    27.         //Same check. This time I'm getting ~0.0016 seconds.
    28.         time = Time.realtimeSinceStartup;
    29.         GameObject.FindGameObjectsWithTag("1");
    30.         Debug.Log(Time.realtimeSinceStartup - time);
    31.  
    32.         yield return new WaitForSeconds(1f);
    33.  
    34.         //Create 20 gameobjects with a different tag
    35.         for (int i = 0; i < 20000; i++) {
    36.             (new GameObject("Tag 2" + i)).tag = "2";
    37.         }
    38.  
    39.         yield return new WaitForSeconds(1f);
    40.  
    41.         //Same check. This takes ~0.0026 seconds.
    42.         time = Time.realtimeSinceStartup;
    43.         GameObject.FindGameObjectsWithTag("1");
    44.         Debug.Log(Time.realtimeSinceStartup - time);
    45.     }
    As you can see, the more gameobjects that are in the scene, the longer it takes to find all objects with a tag. Adding objects with a different tag also adds to the time, so the increased cost is not only due to having to fill a bigger array. Having 20k objects with the tag "1" and finding all of them takes half as long as finding the same 20k objects when there's 20k other objects in the scene. That really smells like searching through everything to me.

    Adding this code to the end of the example:

    Code (csharp):
    1.  
    2.         yield return new WaitForSeconds(1f);
    3.  
    4.         (new GameObject("Sole with 3")).tag = "3";
    5.         yield return new WaitForSeconds(1f);
    6.  
    7.         time = Time.realtimeSinceStartup;
    8.         GameObject.FindGameObjectWithTag("3");
    9.         Debug.Log(Time.realtimeSinceStartup - time);
    10.  
    11.         yield return new WaitForSeconds(1f);
    12.  
    13.         time = Time.realtimeSinceStartup;
    14.         GameObject.FindGameObjectsWithTag("3");
    15.         Debug.Log(Time.realtimeSinceStartup - time);
    16.  
    Gives approximately the same results for finding the single object tagged "3" as the 20k objects tagged "1". There's no way

    These numbers would not be showing up if you were correct, @FuguFirecracker. I also tried setting the singular object tagged with "3" as the first sibling (ie. top of the hierarchy), and it didn't seem to impact the time it took for FindGameObjectWithTag noticeably. That suggests that FindGameObjectWithTag actually finds all and picks the first, instead of doing a linear search.

    These operations could all be improved by having a tag-to-object dictionary behind the scenes. But that would add an overhead to all object creation and destruction, which means that it would slow everybody's code down by a tiny bit, all the time, in order to make searching by tag faster. Finding by tag is already a bad idea, so sacrificing any performance to make it better is probably not something Unity wants to do.


    By the way, the "slowness" of finding by tag is not due to the cost of the operation in itself. You'd never notice a single one. It's when you compare between assigning a single MyScript in the inspector to doing this in the object on a script that gets used a bunch:

    Code (csharp):
    1. MyScript myScript = GameObject.FindGameObjectWithTag("myTag").GetComponent<MyScript>();
    the cost will start adding up. As I've said, if you're doing the Find in Start, the cost is irrelevant (unless you're instantiating a ton of this script). Then it's just bad because tags are inherently unreliable.
     
    FaffyWaffles and lordofduct like this.
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Using a direct reference of FindWithTag are really two different functions for two different jobs.

    A direct reference will always be faster. Following an inspector reference is basically just following a pointer. It's almost as quick as operations come. A direct reference is normally best used when the object is known ahead of time, and doesn't change much.

    The tag system is for more dynamic searches. It's typically for finding a group of similar objects, but it can be used to find a single object. It makes the most sense to use when the object you are looking for can change.

    There are also a thousand other ways to find specific objects, depending on your specific project needs. Of note are singletons, GameObject.Find, Tranform.Find, FindObjectsOfType, manager classes. Each method has pluses and minuses, choose the one best suited to the problem to be solved.
     
    wanrclhnyd and mango_khan like this.
  8. FuguFirecracker

    FuguFirecracker

    Joined:
    Sep 20, 2011
    Posts:
    419
    I stand corrected... The numbers don't lie.
    My apologies for repeating misinformation.
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,181
    No problem! I've done it myself a bunch of times.

    @BoredMormon is of course correct, use the tool most fit for the job.

    I generally avoid tags like the plague, but we're a mid-sized team, so using them would mean that everyone would have to follow rules about what tags to use for what. That would mean that we would have to make the rules, and put them in a document, and make sure everyone follows those rules, and be mean to people who didn't follow the rules. All of those things are terrible things.

    Juggling the layers is already a F***ing nightmare. You have no idea. And they only impact two well-defined things; physics and lighting. Tags are strings that impact who knows what. Every time you want to change a tag on a prefab, you have to know exactly what your code does with both the tags you change from, and the tags you change to.

    If you're a single developer, those rules lives in your head, so it might be viable to have a bunch of strings (tags) around with defined meanings.