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. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Wiring Up GameObject in Inspector vs. GameObject.Find

Discussion in 'Scripting' started by Matt-K, Oct 26, 2018.

  1. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    Hey everyone,

    I've never posted here before but I'm stumped. I wanted to try and avoid using GameObject.Find, and from my reading this is kind of a heated debate and it seems using Find for these two instances in my project wouldn't be harmful. However, I'm dissatisfied with just accepting the fact GameObject.Find is working perfectly but wiring up in my inspector isn't.

    I'd like to learn from this and I'm pretty stumped.

    What I'm doing is:
    When Player dies (lives variable decremented) and new lives value passed to UIManager script component of Canvas to display amount of lives remaining.

    Player:
    Code (CSharp):
    1.  
    2.  
    3. [SerializeField] private int _lives = 3;
    4.  
    5. private UIManager _uIManager;
    6. [SerializeField] private GameObject _canvas;
    7.  
    8. void Start()
    9.     {
    10.         _uIManager = _canvas.GetComponent<UIManager>();
    11.  
    12.         if (_uIManager != null)
    13.         {
    14.             Debug.Log("UIManager isn't null");
    15.             _uIManager.UpdateLives(_lives);
    16.         }
    17.     }
    18.  
    19. public void PlayerLife()
    20.     {
    21.         _lives--;
    22.         _uIManager.UpdateLives(_lives);
    23.     }
    24.  
    25.  
    UIManager:
    Code (CSharp):
    1.  
    2. public class UIManager : MonoBehaviour {
    3.  
    4.     public Sprite[] lives;
    5.     public Image livesImageDisplay;
    6.  
    7.  public void UpdateLives(int currentLives)
    8.     {
    9.         Debug.Log("Player lives: " + currentLives);
    10.         livesImageDisplay.sprite = lives[currentLives];
    11.     }
    12.  
    13. }
    14.  
    The Debug.Logs work perfectly with either:
    _uIManager = _canvas.GetComponent<UIManager>();
    &
    _uIManager = GameObject.Find("Canvas").GetComponent<UIManager>();

    The problem is, the canvas doesn't update the images representing the amount of lives left with when I try to not use Find.

    I'm just curious why this is happening.

    Thank you so much for your time!!!!
     
    Last edited: Oct 26, 2018
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    So nowhere in your post do you mention in what manner "_canvas" is not working.

    Do both options work? Both find, and the via inspector?

    If so, what's your question?
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,738
    Like @lordofduct, I'm having trouble nailing down your exact problem and the lack of code tags is mangling your post, but I can confidently say that, regardless, GameObject.Find is the wrong answer. It always is. In 12 years of using Unity professionally I have literally never seen a time when GameObject.Find was the right way to solve a problem.

    And I'm so sure about this that I once wrote an article about the many reasons not to use it and what to use instead. This is probably a good use case for the singleton approach I think. BUT, be sure to read the article on the caveats of using singletons before you do, because it's really easy to get into bad habits with singletons and use them where you shouldn't.
     
    SteveHoward likes this.
  5. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    Thank you, I edited my original post and hope that's good enough.

    I apologize, thanks for pointing that out. I was all flustered and quickly wrote the post lol. I updated it and hope it's more clear. Just to be clear here; I've got an Image child element of Canvas that I'm using to display little ships to represent the amount of lives my player has.

    If I use GameObject.Find("Canvas"), everything works as it should and the images are in synch with player lives and update.

    If I make a variable of my Canvas GameObject in player and attempt to reference it that way, everything works except the image updating. The Debug.Log's fire and reflect all is working behind the scenes except the image updating.
     
  6. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    I read the article. Some of it went a bit over my head, however it seems like what I'm trying to do here is:

    Public member assignment via Inspector

    Code (CSharp):
    1.  
    2. public class CreepyFollower : MonoBehaviour {
    3.    public Transform target;
    4.  
    5.    void Update() {
    6.        transform.LookAt(target.position);
    7.        transform.Translate(Vector3.forward * Time.deltaTime);
    8.    }
    9. }
    10.  
    I guess why I'm confused is because the scripts are communicating and my code is working. The only thing that isn't happening is the image isn't being updated by the Canvas.
     
  7. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    The only time it's OK to use Find is on Awake() and Start() on stuff that need initializing on program start(or new level start).

    when you start shooting these off for stuff like game events it's always a bad thing.
     
    Joe-Censored likes this.
  8. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    The only reason I questioned my instructor on this GO.F decision was because I had read to avoid them and I wanted to practice avoiding GO.F. Thank you for replying!
     
  9. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    If you need further reasoning, it's because GO.Find goes over all object in the scene and that, while very well optimized in unity 5+ it still takes a while.
    if you do this on initialization(like i've said before) it's ok because its normal for a program to take a few seconds to load.

    but if you have a reference it doesn't have run a search(and doesn't slow you down during actual gameplay), and while it does take up a little bit of memory unless your on a tight budget for some reason(maybe low end mobile) it's negligible.
     
  10. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    Is the issue I'm having something related
    I see. Do you know why I would be having this issue with my canvas' image element only updating if I use GO.F?

    My instructor in this course, is using GO.F like mad for multi-script communication every frame. It's wild. Not only that we're instantiating and destroying objects left and right every few seconds.

    I built a parallel version using an ObjectPooler and the FPS is pretty remarkable. I wanted to see the performance differences between the methods. In my version with the ObjectPooler I'll need to avoid using GO.F to have a good contrast.
     
  11. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    If what I've posted isn't good enough for anyone to understand fully what's happening and make a sound observation I'd be more than happy to provide whatever you'd need. I'd really like to learn from this instance with the canvas.
     
  12. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Perhaps you've linked the wrong instance or the prefab. Make sure it's the correct instance from the scene hierarchy.

    Also, I'd ask the instructor why he's using GO.Find everywhere. It's a bad habit, no matter where it is used - there's always a better solution.
     
    Kiwasi, Matt-K and SparrowGS like this.
  13. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    IT;s ac=
    I’m properly confused on instance vs prefab. I’ll go do some reading on this. Perhaps it’s my issue.

    I decided to try variable assignment on two other GameObjects, one which calls a Coroutine on space bar and I get an error saying the object is Inactive. Even though visibly it isn’t. I used Debug.Logs to check active status before and after spacebar and it’s true before spacebar, the call goes to the coroutine method and then the object is called inactive.

    I’m grateful for the opportunity to learn this and get past it now.
     
  14. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Prefabs are objects that you dragged and dropped into the project window. You can find prefabs in almost every project. The idea is that you construct your objects and then turn them into some sort of blueprint, that can be instantiated either during scene building, or through code.
    Of course, scene objects can be duplicated as well, but they belong to the scene, whereas the prefabs are project assets and hence they're available in your entire project.

    As an example, if you build your scene, you'll usually get into the situation that you import a model (or create an object in the scene), say a door, and the door needs a collider, a trigger area, perhaps a door script, some changes to its material or other components.

    This is a setup that you might need multiple times, possibly in multiple scenes, so you want to save this as a compound object / object hierarchy with pre-defined components and certain values. You'd drag&drop the instance from the scene into the project, and now it's become a complex asset that is easy re-usable.
    Later on, you can always get back to that prefab and re-use it. When you create instances (either during scene building or at runtime), the instances can maintain their own individual state. And you can always reset them to match the state that was saved with the prefab.

    The prefab system has recently faced a major revision+update, and is now even more powerful than before - but the above is the basic idea. Enough to get started. Defintely something that should have been covered by your instructor. I'm surprised this is new to you.

    Coroutines are indeed bound to gameobject's active state, i.e. they cannot be run / they are stopped when the object is either destroyed or deactivated. Looks like your code sets the object inactive.
     
  15. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,738
    Performance isn't the only reason to avoid GameObject.Find, and using it in Start() doesn't avoid its other problems (which are outlined in the article I linked above).
     
    Ryiah, Kiwasi and lordofduct like this.
  16. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    I'm just flat out confused. I've tried variable assignment, but one of my GameObjects calls two coroutines onkeydown spacebar. Even if I set it to active, it has no parent and doesn't visibly deactivate it is false on spacebar. All debug log calls show it as active until the instant I push spacebar even if I setactive(true) on spacebar before coroutine calls.

    Lol..... GO.Find works perfectly. I don't see how I'm doing variable assignment wrong in terms of getting the component and wiring it all up. I'm not confused about instances and prefabs (at least I think). The scripts communicate and everything is working find so I don't think it's my variable assignment.

    I've googled and read TONS of other peoples "coroutine could not start because game object is inactive!" and it's a rule regarding coroutines I'm failing to grasp? GO.Find NO errors? Lol.
     
  17. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    So as for prefabs I was solid on what they were, I have even instantiated a bunch of things but I guess calling the active version of my prefab an instance confused me (I'm not sure why at this point). Instantiating.. instance of prefab lol..

    I appreciate your breakdown on it though.

    GO.Find will call and fire the coroutines perfectly fine, even trying to setactive(true) won't work and the GameObject is reported as activeinhierarchy and is visibly active before spacebar is pressed to fire coroutines.

    GO.Find will work perfectly. When I try to use variable assignment to not use GO.Find, it breaks. I must be wiring them up wrong. I don't know. I feel like I'm chasing my tail.
     
  18. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    Okay, I watched this video https://unity3d.com/learn/tutorials/projects/2d-roguelike-tutorial/writing-game-manager.

    I created a singleton I guess?

    I made my main camera instantiate the SpawnManager (this has the coroutines).
    Then, my GameManager on Spacebar down instantiates my Player
    My Player communicates to SpawnManager to run coroutines that spawn enemies and powerups.

    I made SpawnManager a SharedInstance. This worked. I'm so confused at this point I'm not entirely sure why. I think I made my SpawnManager a singleton??
     
  19. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    So we're running out of options. Can you add a screenshots that shows how you linked the instance to the field in the inspector? Pretty sure it's just a small detail that we're missing here.
     
    Ryiah likes this.
  20. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    It could be that you are updating the wrong UI Manager, in the Debug functions there is an optional parameter called "context", if you pass in "this" as that parameter in your scripts it will allow the debug console highlight the object that issued the debug log when you click on them. You can use this to confirm that the right objects are being called.
     
    Kiwasi likes this.
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I tend to allow it anytime after lunch on the final day of global game jam. ;)

    There are also a couple of niche editor applications where its nice to use.
     
    Munchy2007, Ryiah and lordofduct like this.
  22. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,731
    I'd insert the word 'permanently' before 'solve' in that sentence. Although I use it from time to time during development, it eventually gets replaced with something else.

    There are some edge case uses in a multiplayer game where it might be employed usefully too.
     
  23. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    What I eventually did, is make my SpawnManager a SharedInstance. So it's not wired up at all. I just call SpawnManager.SharedInstance.StartSpawnCoroutines() and it works perfectly.

    Any downside to doing this? I don't see how it could be bad form later on. I rather like the idea of my management scripts being loosely coupled like this.
     
    Munchy2007 likes this.
  24. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    This https://docs.unity3d.com/ScriptReference/Debug.Log.html helped me understand your post.

    When I do logs I do my best to specifically target what I'm trying to check and I had been doing this to make absolutely sure what's going on.

    I have a feeling what I was doing was 1) being frantic in my attempts to use variable assignment instead of GO.Find and 2) mixing up assigning the script component and the GameObject. I was attempting to wire up both and call both, I created two similar variable names and was getting confused. I had figured I could wire up the game object, get component and bam. However, I'm not sure in my flurry of do - test - repeat in rapid succession without really writing down or being methodical I made a mess.

    I used to be a motorcycle mechanic and in my early days I would just buy parts after very shallow troubleshooting and this led to so much unnecessary time spent and head aches. I went about solving this problem in much the same way. I'm going to go back through it slower today and document my attempts and triple check everything.
     
  25. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    I just want to say thank you so much to everyone pitching in here and offering advice and your personal time. It really means a lot to me! I want to be a good game developer and these obstacles and frustrating moments are rich moments of discovery and I really appreciate all of your time and advice!
     
  26. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,731
    I take this approach quite often when I'm working with things that I know will only ever require one instance for the lifetime of the game. I often make all their public methods static as well.
     
  27. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    Okay, that's good to know. It made sense to me to do it that way, it also worked perfectly and felt like less effort.
     
  28. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    This attempt is me assigning the SpawnManager GameObject as a Prefab in a SerializedField to my Player
    Then getting the component, null checking and calling the Coroutines on Spacebar.
    The error is that the SpawnManager is inactive.

    Insight in what's happening:
    Player is not in scene.
    Spacebar down: Player instantiates and in Player > Start():
    • Get handle on SpawnManager script
    • Call method StartSpawnRoutines()
    Then in SpawnManager > StartSpawnRoutines()
    • StartCoroutine(EnemySpawnRoutine());
    • StartCoroutine(PowerupSpawnRoutine());

    The first screenshot is pre-spacebar.
    The second is post-spacebar with SpawnManager selected.
    The third is post-spacebar with Player selected.
    The fourth screenshot is my Player prefab Inspector.
    The fifth screenshot is my Player code in regards to the SpawnManager.

    Screen Shot 2018-10-29 at 8.26.42 AM.png Screen Shot 2018-10-29 at 8.26.58 AM.png Screen Shot 2018-10-29 at 8.32.23 AM.png Screen Shot 2018-10-29 at 8.32.45 AM.png Screen Shot 2018-10-29 at 8.23.39 AM.png
     
    Last edited: Oct 29, 2018
  29. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,738
    Ah, true. In my bigger write-up I do qualify it with "in runtime", but yes occasionally it is useful in Editor code.
     
  30. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    What do you both mean by this?
     
  31. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Google 'problems with singletons'. You'll find plenty of potential downsides.

    Sometimes programming is a case of choosing the least annoying downsides.
     
    SparrowGS likes this.
  32. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You can write helper scripts that only run in the editor and not in the game. This lets you extend the editor to work in weird and unusual ways. Its one of the most powerful features of Unity.
     
  33. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,738
    Most results there are for very general situations, which is not going to be helpful here. I would probably never write a singleton outside of Unity. Within Unity, however, there are plenty of things a singleton can do that a static class can't, such as run coroutines or update loops, have object references assigned in the inspector, or simply be part of a key aspect of the hierarchy (e.g. the main UI).

    There are certainly downsides - it still tends to create tightly coupled code, it's easy to use in a situation where there might actually end up being more than one instance - but overall, singletons in Unity are far, far more useful than singletons elsewhere. And it's one of those situations where, as long as you're aware of the downsides, they're not difficult to sidestep.
     
    Kiwasi and Munchy2007 like this.
  34. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    I see. Apparently one issue is transparency, I guess I could see that.

    As for my game management scripts, I have a hard time seeing why using Singletons would be bad, most likely due to my lack of experience. The dependencies won't be transparent from the Unity editor this is true, but I guess I can comment in my code and make as visible as possible what's at work.

    The SpawnManager could be instantiated and the coroutines could absolutely be called or started in a different way as opposed to depending on my Player. I could eliminate that dependency. However, the enemy and power-ups would depend on the SpawnManager still.
     
  35. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Agreed. Just making sure the OP is aware they exist.

    In particular one thing I find useful with singletons is to keep the instance variable private, and expose only static methods. Something like this:

    Code (CSharp):
    1. public class MySingleton : MonoBehaviour {
    2.     private static MySingleton instance;
    3.  
    4.     // regular singleton implementation
    5.  
    6.     private void SomeMethod () {...}
    7.  
    8.     public static void SomeMethod () {
    9.         instance.SomeMethod();
    10.     }
    This makes calling code cleaner. No need for the extra .instance. You can simply call

    Code (CSharp):
    1. MySingleton.MyMethod();
    It also makes the code simpler to refactor. If you decide not to implement it as a singleton any more, for whatever reason, you can simply redirect the static method.
     
    SparrowGS likes this.
  36. Matt-K

    Matt-K

    Joined:
    Sep 10, 2018
    Posts:
    79
    That's cool! Some of it confuses me, I'll need to spend time with it but cutting out the .instance is nice!