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

Having Static Variables; What's the Best Practice?

Discussion in 'Scripting' started by ReedRGale, Dec 2, 2016.

  1. ReedRGale

    ReedRGale

    Joined:
    Nov 30, 2016
    Posts:
    5
    Hello!

    I'm new to Unity and I was making two scripts that should work in tandem, containing the same acceleration, deceleration and maximum velocity. The way I solved this problem is that I made a script which I made an instance of inside of a GameObject that I passed into the scripts that needed references to those variables.

    ...but that's just me bodging a solution out of nowhere.

    Is there a better way to handle static or static-like variables when working with Unity? Ultimately, all I need is to edit the values in one place and then have that affect multiple scripts in an efficient way; answers and links to other forum posts of similar problems are appreciated.
     
  2. JC_SummitTech

    JC_SummitTech

    Joined:
    Nov 1, 2016
    Posts:
    78
    First, not everything needs to be a MonoBehaviour. Having plain classes can be useful for stuff like that.
    You can also take a look at ScriptableObjects, depending on your needs.
    If what you need to store is only a bunch of app-wide variables, you could make a class, that is a singleton and contains your static variables.

    ie:
    Code (CSharp):
    1. public class MyAppWideManager
    2. {
    3.     private static MyAppWideManager  instance= null;
    4.     public MyAppWideManager Instance
    5.     {
    6.         get
    7.         {
    8.             if(instance == null) instance = new MyAppWideManager();
    9.             return instance;
    10.         }
    11.     }
    12.  
    13.     public int myVar = 1;
    14.     public float myFloat = 333.3f;
    15.    //etc
    16. }

    usage looks like Debug.Log(MyAppWideManager.Instance.myVar);
     
  3. ReedRGale

    ReedRGale

    Joined:
    Nov 30, 2016
    Posts:
    5
    Thank you for the quick response!

    The answer works as expected; for coding purposes, I was hoping that you'd be able to answer one more question. What is the relationship between "MyAppWideManager" and "Instance" on line 4 in OOP terms? Is Instance in MyAppWideManager? Is it an odd form of constructor?

    I come from Java and I'm just hacking this C# thing, but I figure that I should just ask questions as I go. Thank you for your prompt response, again. ^u^
     
  4. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    Instance is a static property of the class. There's a typo though, it should be:
    Code (csharp):
    1. public static MyAppWideManager Instance
    Also note that in this case, you could just have the other properties themselves be static:
    Code (csharp):
    1. public class MyAppWideManager {
    2.   public static int MyIntValue;
    3. }
    4. ...
    5. MyAppWideManager.MyIntValue = 5;
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,194
    It's a lazy singleton!

    It's a bit of a half-assed singleton implementation (sorry, @Errorsatz). A proper one should have a private constructor so it's actually guaranteed to be the only instance, and should (probably) give access to it's fields through static methods rather than having the instance be public.

    Be carefull with singletons - they have a tendency to make following flow difficult. A good example of a good use-case would be to wrap the player's settings or whatnot.


    If you want to scripts to work together, you can just have them talk to each other. If script A and script B needs to have the same values, you can just put them in the script where it's most natual that they belong and read it off in the other. Encapsulate as you'd normally do.

    If you post more about your problem, we can give more specific advice.
     
    lordofduct and Kiwasi like this.
  6. JC_SummitTech

    JC_SummitTech

    Joined:
    Nov 1, 2016
    Posts:
    78
    As Baste said, it's a singleton. Feel free to call it lazy :p
    "That line" defines a property. In java you would do getInstance() and setInstance(MyAppWideManager mgr), in C# there is an elegant syntax where you put
    MyType NameOfMyProperty
    {
    //optional
    get{ return nameOfMyVariable;}
    set{ nameOfMyVariable = value;}
    }

    This allows to set a getter and setter, and you could make them public or private however you see fit. Using a name such as myVariable and a property name where you simply capitalize the variable name (MyVariable) is sort of a standard but not required.
     
  7. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    in all honesty "best practice" is relative. in some projects, if its effective and performant then it doesn't really matter if you're using best practice.

    that said nearly every example on the use of Singletons, especially in Unity, is far from best practice. From a software management perspective, I see Singletons as risks to future development. They're not naturally thread-safe, inherently tightly coupled, complicate unit testing, and make polymorphism nigh-impossible.

    that doesn't matter 95% of the time because most Singletons written in unity are for relatively small projects with small teams, where project complexity is low. still doesn't make it a best practice.

    Static variables is a different story. they're best reserved to hold data that all instances of a class can share. For example a scene can have many Cameras, but only one can be considered the "main" camera.

    For your situation, however, I believe ScriptableObject assets are what you are looking for. create a scriptableObject class with some data and then write your MonoBehaviour scripts to be able to store a reference to that class. boom multiple scripts can now hold a reference to the same data. Plus since that reference is an asset the data can persist across scene effortlessly and can also be referenced in prefabs.and in the event you need the same class but with slightly different data, then you just create a new instance have the new scripts use the new instance, without it breaking your old script still using the older instance.

    For example I have a SelectionModelData class that inherits from a ScriptableObject and all it does is hold a current selection of type UnityEngine.Object and it'll fire a UnityEvent the moment that current selection changes. With this one class, used in three completely different games. its used to store:
    • the preview of a defense emplacement during its placement
    • the selected defense for upgrade/selling
    • the data needed for a level (decoupled from the level itself so that things like level select can affect data in a scene)
    • the current vehicle the player is driving
    • the current vehicle within a specific faction the player is driving
    • the current camera system to use (1st person, 3rd person, RTS, etc.)
    • the current command to run.
    • the spell that is equipped
    • the primary target for AI to focus
    and dozens, and sometimes hundreds of scripts will want to reference the same instance for each of these examples (from UI to AI to Logic). all of this is handled by one simple 30 line class, with no sacrifice to Unit testing, coupling, or polymorphism that would have happened by using Singletons
     
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Just to clarify, lazy is a technical term, not a comment on the code quality. Lazy indicates that the object isn't initialised until the first time it's used. It can be a useful tool in the right circumstances.

    On the other hand half-assed isn't a technical term. This is a direct comment on code quality.
     
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Though I agree with the private constructor, as well as the warnings about singletons.

    I would beg to differ on the argument for the fields to be uncovered by a static method/property.

    My reason is that if you did... why not just make a static class?

    The point of a Singleton is to have the benefits of being both global accessible with ease (like static gives you), while maintaining object identity.

    You can't hand around a reference to a static class... but you can hand around a reference to a Singleton.

    So lets take an example that I have on hand in SPFramework:
    https://github.com/lordofduct/space...blob/master/SpacepuppyBase/PsuedoSingleton.cs

    This is a psuedo singleton, it's job is to allow you to create a Singleton out of an existing class that isn't a Singleton in and of itself. The reasons for its existence may seem weird... but basically my artist wanted a way to access a global reference through our visual scripting tool I made for him, but for components that aren't inherently singletons (like... a player controller or something). Anyways, that's not the point of this....

    Note that it implements the 'IDynamic' interface.

    This interface also works with the visual scripting engine I wrote. This way properties and methods can be reflected off of stuff dynamically at runtime.

    Thing is... it's a global. If it were static, these methods would be innaccessible since we couldn't get a reference to it. So instead as a Singleton, it has object identity, I can reference it AS A IDynamic, and use it in that manner.

    Now functions that ask for an IDynamic or a Component or whatever type this Singleton inherits/implements, we can pass it in.



    Of course there is one OTHER reason to create a Singleton in Unity if not for object identity (directly)... but rather to gain access to the Unity events, which inherently require object identity.

    Ala singletons like this of mine:
    https://github.com/lordofduct/spacepuppy-unity-framework/blob/master/SpacepuppyBase/GameLoopEntry.cs

    Which in yes, I do offer out most of its public interface as static methods.

    But they aren't static because of the habit of how I implement singletons, but rather in opposition to the habit of how I implement singletons.
     
    Kiwasi likes this.
  10. JC_SummitTech

    JC_SummitTech

    Joined:
    Nov 1, 2016
    Posts:
    78
    I thought he meant lazy because I didn't include a private constructor (which is technically true). Not that I take offense. It DOES make more sense to take lazy in the sense of lazy initialization thought lol
     
  11. JC_SummitTech

    JC_SummitTech

    Joined:
    Nov 1, 2016
    Posts:
    78
    Well yeah, best practice is relative, but I found that singleton pattern very useful in Unity. Have a couple "manager" singletons that keep reference to objects of self-contained systems, and you get a nice easy to manage and decoupled system. It certainly beats having FindObject calls ;)


    I beg to differ with the second part of that statement. What you are describing sounds more like a static class than a singleton, and while it is an acceptable practice, it has some drawbacks, namely that The static class is never unloaded (you could create and destroy a singleton when you need/don't need it) and static functions always reside in memory. For those reasons I generally prefer using a singleton instead of a static class.
     
    lordofduct likes this.
  12. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    I believe your definition of easy to manage and decoupled are greatly different from mine.

    What if you made a GameController singleton for your game and then suddenly the design adds a new GameMode, a new level of difficulty, or simply just a new mechanic that has a large impact on the game. if all that code gets stuffed into the one, single GameController class then there's more chances for things to break and it ends up turning into a god class. it'll be tougher to test for all the edge cases for a specific game mode if another game mode's implementation is in the same class. normally you'd fix this by simply making a new instance of the class which holds different data but singletons won't allow that, just the one instance. By my definition that's not easy to manage.

    Can you call a singleton without referencing the concrete class? No because you have to access it through a Static call which is concrete specific. Inheritance won't work, neither will polymorphism, This means that every class that wants to use it must know the exact class to call.which by my definition is not decoupled.

    now I've written code to access singletons abstractly, thanks to the power extension methods. However I've found its far simpler to just use injections

    I'm not denying the usefulness of singletons, they get the job done. Its just they aren't the best thing for that job especially on more complex projects. sure it beats Find Object calls, but so does injection which is just as simple to do (in many cases even simpler) while also a ton more manageable.
     
  13. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,802
    I use tons of MonoBehaviour singletons for lots of different things. I find them extremely useful when kept modular and focused on their responsibility. I have AudioManager, LevelManager, AchievementManager, InputManager, TweenManager, SaveLoad, and the list goes on. They're all loaded in my first scene, and persist throughout the game. These are all either my own implementations or wrappers for other libraries and engine features using my preferred api style. They are not tied to or specific to any level or mode.

    I've essentially built my own top layer for the engine to make new content extremely quickly because all the tools I need are built. LevelManager will change levels using a custom Transition object. AudioManager can do fades and pitch changes over time automatically, and handles all the sound effects for the game as well. InputManager unifies the controls between touches and mouse, creating a single interface for all our platforms controls. TweenManager is a big wrapper for DOTween and makes motion and easing with callbacks a breeze.

    They're all super generic and would work with any game.

    At my job, we are constantly adding new features and drastically redesigning mechanics. To me your example means you're doing it wrong and didn't design your Singleton with enough generic functionality to extend it. The GameController wouldn't handle or know about GameMode-specific data. That would be the GameMode's responsibility. The GameController would handle general game functionality and interface with GameModes in a generic way and support any type of GameMode.

    I have never needed to subclass a Singleton. The Singleton should be a global and generic tool to be used in any context, not for specific things in the game that will break when you change something game-related.

    All opinion obviously, but you can badly design any system. I guess singletons are easier to abuse for those less experienced with them, thus the never ending stream of warnings against Singletons.
     
    Last edited: Dec 6, 2016
  14. ReedRGale

    ReedRGale

    Joined:
    Nov 30, 2016
    Posts:
    5
    Thank you for all the discussion! I think this is really helping me to understand this concept. ^u^

    Letsee if I understand what's being said:

    Using Singletons is dangerous because it's a global object. You go and make just this one instance of an object that can then be used everywhere. It is an object which has useful properties, like being able to be easily moved around, referenced and destroyed, as well as purposes in implementing interfaces (so it has it's place sometimes for reasons related to polymorphism). The point is, if I'm going to use the Singleton pattern, make sure that the Object has a singular, generic purpose.

    While you can hand a reference of a Singleton around--which has power--usually it's just better to have a static object because they serve about the same purpose and make following the flow of code more simple because you simply use the class to call its static methods or data and do what you ask it to do.

    Alternatively, there exists ScriptableObjects which exist in Unity and, if I understand them right, they're basically an Object of their own that you don't attach to other GameObjects--it's logical and holds data and can be referenced by other GameObjects, but only really holds data, meaning it doesn't need a Transform or spacial location.

    Personally, looking at all the options, I believe that having a ScriptableObject seems to be the best option in my case.

    What I'm trying to do is determine the physics of how the player should move in a 2D side-scroller and have the Camera move with similar properties, as well as possibly "follower" characters. As such, I need to store similar variables for all these GameObjects and I would like to have all of the logic stored in one place so all of these Objects follow similar logic. I was going to have all of these objects reference the player, but I feel like decoupling "Movement" information from the Player object itself has value for my particular problem.

    Again, thank you for all the input! :D

    Ah, but I did want to ask, @JoshuaMcKenzie is this what you meant by "injections?"
     
  15. JC_SummitTech

    JC_SummitTech

    Joined:
    Nov 1, 2016
    Posts:
    78
    Unless I reaaaally misunderstood ScriptableObjects, they are an entirely different beast. They are basically data containers, not meant to be an "active" element in your scene. You can see them as database entries. They store complex data for large objects. Maybe you could use them as a manager object... but I wouldn't recommend it.
     
  16. ReedRGale

    ReedRGale

    Joined:
    Nov 30, 2016
    Posts:
    5
    @JC_SummitTech

    That's the general idea I'm getting as well. Perhaps I misrepresented myself, but in this case, that is all I need: something to store data relevant to player or player-like movement. There are no methods or further functionality that this object needs to handle and, as such, it seems to be best defined as a ScriptableObject.
     
  17. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,131
    Well, I am not a c# / oop expert either. But In my understanding (possibly not expresses with the right terminology), you are always creating instances of a class unless you use static (then you access the class function directly)

    A singleton always is an instance (but it allows only one to exist and you access it with a static reference) - this can be an in scene singleton if it derives from monobehavior or a normal c# class.

    A mono class attached to a game object in the editor is also just an instance of a class, but not limited to one only.

    A scriptable object is pretty much the same as a normal class but it is not instantiated by attaching it to a game object but either via script or with the create in the editor. If you pass it a reference to an in scene object it can very well run all the logic for it.

    In the end they are all pretty close. I started to use SO for certain global variables as it felt better to have them in the game assets. I always screwed up changing them in the inspector.
     
  18. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    Singletons basically try to figure out what (one) instance they should be using. Its their responsibility to determine and set that instance. other classes tend to use things like FindObjectsOfType() or GetComponent() to grab the instance that they need, they made it their responsibility to get that instance.

    whereas Injection means that the instance is given to them from an outside source. its "Injected" into the class and thus it is the responsibility of the outside source. if that script fails due to a missing reference its not the fault of the script, but the outside source. this makes the script's responsibilities more clearly defined and resistant to bugs, as when such a missing reference happens the script usually doesn't need to be modified. At minimum the script may post a warning and return early to prevent anything catastrophic.

    simply put, when you have a public field in a monobehaviour and you set that field via the inspector, you are injecting an instance into that monobehaviour. the monobehaviour isn't trying to figure out what instance to get, you handled a (potentially expensive) task when you set the field that the script doesn't have to worry about.

    This concept is also called DI(Dependency Injection) or IOC (Inversion of Control). Instead of the script having control over what its working on, an outside source handles the context of the scripts use. In practice this makes the scripts a lot simpler and less error-prone. They become easily digestible and reusable in more generic cases.

    Injection is usually handled by a context class (class that handles all, or nearly all interconnections between other classes in a specified environment/scene), or more simply manually setting up the references in the inspector for each scene. So in one scene you can have scripts referencing one scriptable object asset and exchanging data to it, while in another scene those same scripts now reference a completely different ScriptableObject instance.
     
  19. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    The biggest and very non-trivial problem with DI is that you give up control over instantiation and you lose access to constructors (generally speaking; the exceptions and tricks aren't worth mentioning here). It strikes me as a pretty bad idea in a game scenario where you can never have too much control.

    I choose statics over singletons when the functionality is all code (no static data), or when the functionality is truly global -- it makes sense for it to be available all of the time, to everyone. Usually that's setup information about the game, player information, and other stuff that the whole app is likely to need.

    I'm always surprised at how often the topic comes up, and I rarely see a clear list of what you get in exchange for the additional gymnastics of a singleton, so even though I don't use them often, I'll take a stab at it off the top of my head:
    • easily controlled constructor (static constructors can be tricky)
    • an instance you can pass around like a regular class
    • interface implementation
    • doesn't use any significant memory until it's initially used
    Static members (properties and fields) in non-static classes are also a great way to share data between instances of a class. It's a perfectly valid usage which I feel the "never use static" folks overlook or discount for reasons I can't understand.

    None are "dangerous" if you know how they work (and they aren't complicated). Actually I doubt they'd be especially dangerous even if you didn't understand them.
     
  20. SUfIaNAHMAD_

    SUfIaNAHMAD_

    Joined:
    Jun 19, 2019
    Posts:
    18
    Is this code called a singleton? And, what it is fine to use static fields instead of making a whole singleton class?
    Code (CSharp):
    1. public class ScoreManager : MonoBehaviour {
    2.     public static int coins;
    3.     public static int distance;
     
  21. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    No, it's not a singleton in the sense of the design pattern.

    Basically you can do whatever works for you. Though some approaches are better than others.

    If the only thing you need is a simple value to add up your coins, score whatsoever, it can be just fine. For instance, a Flappy Bird type of game could get away with it even when it's generally not the best approach... but again, for these simple things, it might be sufficient.

    Note:
    Please be aware that the thread's not been used for about 4 years. Try to avoid posting in "inactive" threads, create a new one and add links to the resources and discussions you'd like to be considered in your own discussion.
     
    SUfIaNAHMAD_ likes this.
  22. SUfIaNAHMAD_

    SUfIaNAHMAD_

    Joined:
    Jun 19, 2019
    Posts:
    18
    Thank You, I will avoid inactive threads from next time :)