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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Singleton GameManager - GameObject References Best Practice

Discussion in 'Scripting' started by solideuk, Oct 21, 2015.

  1. solideuk

    solideuk

    Joined:
    Nov 29, 2014
    Posts:
    41
    Hi All,

    I need some advice on whether my current GameManager set-up is correctly set up.

    I've created a singleton script with a DontDestroyOnLoad function on an empty GameManager object.

    I intend to use the GameManager to store references to other gameobjects such as the player. As the player is already on the scene at start of the level, I have dragged the player object into the GameManager inspector.

    On start everything works fine. However, when I reload the player object reference in the GameManager inspector field is missing, presumably because the Player object gets destroyed.

    To address this, I am considering using a OnLevelWasLoaded function in the singleton script (GameManager) to find (FindByType) the player object.

    As the player gameobject will need to be accessed by other gameobjects, I plan to allow the other gameobjects to access the reference by going through the GameManager, using GameManager.instance.player;.

    If the above is correct, will the function GameManager.instance.player enable other scripts to ‘instantly’ gain access to the player game object reference? As certain objects need to access the player object on awake.

    Is my perception on the role of a GameManager correct?

    Apologies if this sounds like a silly question, I’m still trying to get my head round some of the main functions.

    Appreciate any help and guidance.
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    There are a few things I can suggest:
    1) Make the .player reference a property, and it finds the player instance on demand (and cache it):
    Code (csharp):
    1. private PlayerScript playerObject;
    2. public PlayerScript player {
    3. get {
    4. if (playerObject == null) playerObject = FindObjectByType<PlayerScript>();
    5. return playerObject;
    6. }
    7. }
    That way you don't need to worry about when the player gets updated, because it'll always happen just in time. And this'll also work if the player object is destroyed by other means.

    2) Make PlayerScript set the reference itself in Awake. This would be most efficient, as it avoids FindObjectOfType entirely (which is pretty slow). You could, by the way, combine 1 and 2 for a method that's usually fast and always reliable.

    3) Take that a step further, and put the reference in PlayerScript directly - that is, make PlayerScript its own singleton. This may be inadvisable depending on your future plans, but then, a reference in a GameManager has all the exact same drawbacks as a PlayerScript singleton.
     
    image28 and solideuk like this.
  3. solideuk

    solideuk

    Joined:
    Nov 29, 2014
    Posts:
    41
    Thanks for your expeditious response.

    For option one, would I attach the PlayerScript on the player object with the view that the reference to the player will be immediately available to other gameobjects?

    How can other gameobjects access the playerobject?

    You see ideally I would like all gameobject references to be stored on a single script (GameManager), that way I do not need to use ‘FindObjectsofType’ in every gameobject that needs to access the player object.
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    You don't need to attach anything anywhere, really. It'll set itself up the first time you try to access it.

    You would access it from other scripts in what I assume is the same way you were planning to:
    Code (csharp):
    1. GameManager.instance.player.somePlayerFunction();

    Yep, and this does that. It'll call FindObjectOfType exactly once total (unless it's augmented by option 2), and then every object will reference the same cached object.
     
    solideuk likes this.
  5. solideuk

    solideuk

    Joined:
    Nov 29, 2014
    Posts:
    41
    Thanks for the very helpful response, will experiment with this.
     
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You've got some good advice on the how from @StarManta. I'm going to wander off into design land.

    I'm always very cautious with this type of manager. They are often only slightly better managed versions of global state. With all of the attendant pitfalls that come with global state.

    Some things to consider
    • Make the reference to the player read only. You don't want other objects changing your reference accidentally.
    • Does everything really need direct access to your player? This open approach can lead to hard to trace bugs.
    • Is there any chance the game will go multiplayer? If so this approach will fail.
    • Code maintenance can be expensive
     
  7. solideuk

    solideuk

    Joined:
    Nov 29, 2014
    Posts:
    41
    Thanks BoredMormon.

    What would be the best way to create a centralised location with important gameobject references, such as a player object.

    Wouldn't this approach be better, as there would be a single location to access references instead of each gameobject using a findbytype command to search for the object. Within this setup the search will be conducted once with the result shared amongst objects that need it.
     
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    If this is what you want, then a singleton is the answer.

    A more interesting question would be how to structure your code to avoid the need for globally accessible references to every significant game object.
     
    Munchy2007 likes this.
  9. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,732
    What then would be the answer to that particular question?

    Personally I'm still unable to get away from the singleton GameManager pattern.
     
  10. sluice

    sluice

    Joined:
    Jan 31, 2014
    Posts:
    416
    It's completely fine to use Singletons. I personally use them in every projects.
    Where the problem lies is when you start to have a bunch of singletons... and a bunch of class communicating directly with them without any logic . Eventually it will become hell to manage, like @BoredMormon said bugs will be hard to trace, the flow won't be logic. In some cases Singletons are a good solution, in other case they are they are the easy/lazy solution and you will pay for it in the end.

    Before deciding this should be a singleton or what your GameManager Singleton should include, (and even before jumping on the computer!)... I would advise on taking a pen and some paper:
    1. Gather the requirements. Write time. What do you want to accomplish? What are you trying to solve? Get Specific!
    2. Identify the main objects and what they generally need to do.
    3. Describe the interactions between these objects. (This is where you determine which object needs to communicate with which one. Each time ask yourself does this object really need to communicate with this one?)
    4. Lastly, once the objects and interactions are defined, drawing up a class diagram is a good idea! (with a UML editor or on paper)
    Taking time to do this, will probably lead you to notice that some components do not even need to communicate directly with each other and In the end you will save time.:rolleyes:
     
  11. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,732
    The steps you describe are pretty much exactly how I approach things at the moment.

    I'm guessing this is as you seem to suggest, okay for small projects, which is all I have worked on thus far. However my suspicion is that for anything more complex I will have to change my approach, which is why an answer to BoreMormon's question would be nice, even if it's only to steer me in the right direction for future research. :)
     
  12. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    The way I think about singletons is this: Is this thing truly global, and and there any circumstances in the future where it might not be? And, even when you decide to use a singleton, it may be in your best interest to not necessarily take advantage of it in every case.

    For example, it may seem straightforward to have a singleton for the Player object. And maybe for your project that's fine. The tricky part of singletons (and their tempting danger) is that, when (for example) something else collides with the player, you may want to use Player.instance instead of otherCollider.GetComponent<Player>(); But what if, a month from now, you decide to add multiplayer? Now you've got two Player objects, fighting over who gets to be Singleton. But all of the code you wrote that's dependent on the concept of having one player object is wrecked and has to be rewritten.

    The problem isn't the singletons; it's the pattern of thinking that leads to laziness. The weird part is, you can change your own pattern of thinking by changing which names you decide to use. Hack your own brain. Instead of Player.instance, use Player.userControlledPlayer. Instead of LevelLandscape.instance, use LevelLandscape.current or .local. MyGUI.instance becomes MyGUI.onScreenGUI. Each time you type out these names, it'll force you to consider the actual purpose of these singletons, and - even if for just a second - that's often enough to keep you from making bad decisions about how they're being used.
     
    vivekhash and sluice like this.