Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

Best way to initialize static class not attached to a GameObject

Discussion in 'Scripting' started by syscrusher, May 6, 2016.

  1. syscrusher


    Jul 4, 2015
    Hello, all

    I have a class that during its initialization creates a singleton List<> and populates it with objects that represent options for things the rest of this class will do later. The class runs in both editor and game modes (that is, it has the [ExecuteInEditMode] attribute).

    The List<> is built during my initialization method, and after I've added all the default options I invoke a virtual method called lateInitialiation() that can be overridden by subclasses to add their own options.

    This all works just fine if the subclass has an instance attached to a GameObject in the scene. Unfortunately, my class also has a fairly complex custom Inspector, and that Inspector is lost if a subclass doesn't also implement at least a trivial version of the custom Inspector.

    What I'd really like to do is to have external classes be able to identify themselves to my class in such a way that I can actively invoke a static factory method in the third party class. The ideal goal is that an extension to my class would *not* be a direct subclass, but would in fact be a static class that has to be compiled into the project but doesn't have to ever be instantiated in the scene.

    The reason I'm seeking to do it this way is that the extension process is literally just a matter of populating constants for procedural geometry into a data object and then adding that data object to the List<> in my main object. The List<> itself is a singleton, so static factory methods to populate it work well.

    I'm thinking that I might be able to achieve this by creating a custom Attribute that third-party classes can attach to themselves at the class or method level. Is this the "right way" to achieve what I need? Or is there a better way?

    Thanks for any advice on "best practice".
  2. syscrusher


    Jul 4, 2015
    After some further research...

    Is a combination of [InitializeOnLoadMethod] and [RuntimeInitializeOnLoadMethod] a good way to accomplish this?

    EDIT: Nope. Tried it, didn't help.
    Last edited: May 6, 2016
  3. Munchy2007


    Jun 16, 2013
    NotHalfBrad and syscrusher like this.
  4. syscrusher


    Jul 4, 2015
    Hey, thanks! I actually used to use those in Java, but I didn't know C# supported them too. I'll try that and post back here with what happens.
  5. syscrusher


    Jul 4, 2015
    Update: The static constructor works as expected on the object, but only if the script is used somewhere in my scene. Just having the script built with the project doesn't seem to trigger even static init.

    I think what I may have to do is provide an event interface in my primary class and have add-ons be manually registered at design time.

    Thanks for the pointer to the static constructor, though. It didn't solve the exact issue I posted about, but it did solve something else for me. :)
    Munchy2007 likes this.
  6. AutoFire-II


    Oct 30, 2015
    I know this is old, but this was one of the best solutions to my problem. (Specifically, the info on static constructors.) I thought I'd post my solution to the original poster's question.

    Anyway, I have successfully created MonoBehaviours that do something upon the game's launch. Below is a modified snippet from one of my C# files. I have a static that maintains a list of these components and automatically adds the component when it isn't set up on the target GameObject. This ensures that, no matter what, my code gets run.

    Keep in mind that there are a couple caveats when using in the editor. If my memory is correct, the constructor in the snippet below runs every time that the code is recompiled, but not at all when the game itself is run. If all you're doing is initializing static things (which should be all that you are doing!), then you should be fine as the editor carry these values correctly into the Play Mode.

    In standalone builds, it gets run while the game boots, but the constructor gets neglected no matter what.

    Finally, I have not tested this solution on mobile devices or consoles. If it doesn't work, it might fail silently. If you need to get this to work on one of those platforms, test it first!

    Code (CSharp):
    2.     #if UNITY_EDITOR
    3.     [UnityEditor.InitializeOnLoad]
    4.     #endif
    5.     public class AlwaysInitializedBehaviour : MonoBehaviour {
    7.         // Notice that these methods are static! This is key!
    9.         #if UNITY_EDITOR
    10.         static AlwaysInitializedBehaviour() {
    11.             // Outside of the editor, this doesn't get called, and RuntimeInitializeOnLoad does NOT
    12.             // support calling constructors. Therefor, we cannot assume this will always get called.
    13.             // This is a good opportunity to do editor-specific things if necessary.
    14.             Initialize();
    15.         }
    16.         #endif
    18.         #if UNITY_STANDALONE
    19.         [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    20.         #endif
    21.         static void Initialize() {
    22.             // I do not remember if the pre-processor check is necessary, but I do
    23.             // know that this code will not get called unless you have the constructor like above.
    25.             // Anyway, put whatever initialization code you want here.
    26.         }
    28.         // Various other things follow...
    30.     }
    With the amount of power this gives, I must warn that this kind of power can lead you into a lot of trouble. I would avoid using such a pattern unless you are certain that it is the best solution. If you aren't up to speed on your OOP programming practices, I highly recommend that you give this (free) book a read before you dive off into such rigid solutions; you may find a better alternative.
    NotHalfBrad and syscrusher like this.
  7. syscrusher


    Jul 4, 2015
    Excellent tips, and thanks for sharing!