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

Can't add C# generic class derived by MonoBehaviour to gameobjects

Discussion in 'Scripting' started by Eugenio, Mar 10, 2014.

  1. Eugenio

    Eugenio

    Joined:
    Mar 21, 2013
    Posts:
    197
    I have something like:
    Code (csharp):
    1. public class MyBehaviour<T> : MonoBehaviour where T is MyBehaviour<T> {
    2.    ... lots of cool stuff
    3. }
    The file name is MyBehaviour.cs .

    When I try to add this script to a gameobject in my scene I will receive the "The script name doesn't match the name of the class" error.
    Am I missing something?

    Thanks :)
     
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    You cannot directly use generic - for good reason - as a Component.

    Code (csharp):
    1.  
    2. public class MyDerived : MyBehaviour<MyDerived>
    3. {
    4. }
    5.  
    The above can be added to a GameObject properly.
     
  3. Eugenio

    Eugenio

    Joined:
    Mar 21, 2013
    Posts:
    197
    To be honest with you I already did something like that but I was wondering if there could be another way to do it.

    Thank you very much for the quick answer.
     
  4. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Lightstriker is right... the other question I would have is about your generic constraint... why are you using:

    where T : MyBehavior<T>

    Wouldn't you be better with something like:

    where T : Vector3 or some other type? You're constraining the type of the MyBehavior<T> class so that T is also a Mybehavior<T>. So you're kind of creating a circular constraint there because it would be something like:

    MyBehavior<MyBehavior<MyBehavior....>>

    I can't see any situation where it makes sense.
     
  5. Eugenio

    Eugenio

    Joined:
    Mar 21, 2013
    Posts:
    197
    I'm using that configuration because I need a singleton of my class.
    If it will be the base class or an implementation, by using that declaration I can create a singleton having the instance as a generic T type in the base class. This will allow me to have always the correct type also if I call the instance from a child as a child type.

    I don't know if this will make sense to you, but doing that I can also instantiate automatically the proper component in a gameobject in the scene with the first call to the class static methods. ;-)

    Anyway you will never have the case MyBehavior<MyBehavior<MyBehavior....>> if you will declare MyBehaviour<T> as abstract. In this case you always need a derived class to let it work properly. :p
     
    Last edited: Mar 10, 2014
  6. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    It's not circular at all, it's a self-referencing generic.

    It enforces that the type of T is deriving from itself.

    So you could do

    Code (csharp):
    1.  
    2. public abstract class Manager<T> : ManagerBase where T : Manager<T>
    3. {
    4.     private static T instance = null;
    5.  
    6.     public static T Instance
    7.     {
    8.         get { return instance; }
    9.     }
    10. }
    11.  
    That way, I cannot do;

    Code (csharp):
    1.  
    2. public class SomeName : Manager<Vector3> { }
    3.  
    Because I obviously don't want T to be "Vector3"

    It's useful for a lot of method that should return that type (itself).
     
    Last edited: Mar 10, 2014
    Bunny83 and guneyozsan like this.
  7. Eugenio

    Eugenio

    Joined:
    Mar 21, 2013
    Posts:
    197
    This is exactly what I was talking about. :p
    The code works better than all my stupid words. :-D
     
  8. Vijay-Kumar-R

    Vijay-Kumar-R

    Joined:
    Jul 5, 2013
    Posts:
    2
    Code (CSharp):
    1. Singleton.cs
    2. ----------------------------------------------------------------------------------
    3. using UnityEngine;
    4. using System.Reflection;
    5.  
    6. public class Singleton<T> where T : class
    7. {
    8.     class SingletonCreator
    9.     {
    10.         static SingletonCreator() { }
    11.        
    12.         internal static readonly T Instance =
    13.             typeof(T).InvokeMember(typeof(T).Name,
    14.                                    BindingFlags.CreateInstance |
    15.                                    BindingFlags.Instance |
    16.                                    BindingFlags.Public |
    17.                                    BindingFlags.NonPublic,
    18.                                    null, null, null) as T;
    19.     }
    20.    
    21.     public static T Instance
    22.     {
    23.         get { return SingletonCreator.Instance; }
    24.     }
    25. }
    26.  
    27. public class SingletonBehaviour<T> : MonoBehaviour
    28.     where T : SingletonBehaviour<T>
    29. {
    30.     protected static T mInstance = null;
    31.     public void Awake()
    32.     {
    33.         if(mInstance != null)
    34.             DestroyImmediate(GetComponent<T>());
    35.         else
    36.             mInstance = GetComponent<T>();
    37.     }
    38.  
    39.     public static T Instance
    40.     {
    41.         get { return mInstance;}
    42.     }
    43. }
    44.  
    45. ----------------------------------------------------------------------------------
    46.  
    47. public class EventsManager : SingletonBehaviour<EventsManager>
    48. {
    49.     public static EventsManager Server
    50.     {
    51.         get { return Instance; }
    52.     }
    53. }
    54.  
    55. public class GameData : Singleton<GameData>
    56. {
    57.     public GameData()
    58.     {
    59.  
    60.     }
    61. }
    62.  
    63. public class GameApplication
    64. {
    65.  
    66.      public void StartGame()
    67.      {
    68.           EventsManager.Instance.TriggerEvent();
    69.           GameData.Instance.LoadData();
    70.      }
    71. }
     
    Fressbrett likes this.
  9. Timelog

    Timelog

    Joined:
    Nov 22, 2014
    Posts:
    528
  10. Eugenio

    Eugenio

    Joined:
    Mar 21, 2013
    Posts:
    197
    Hello guys and thanks for your replies.
    Your posts are really interesting and helpful so thanks for sharing :)
     
  11. MultidimensionalShow

    MultidimensionalShow

    Joined:
    Jul 6, 2012
    Posts:
    22
    This just saved my live. Thanks!
     
    AldeRoberge likes this.
  12. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    83
    Very intersting stuff. I always added my own singleton logic to my behaviors, but inheriting from a generic SingletonBehaviour class makes a lot of sense.

    One small addition: It probably makes sense to make the Awake function within the SingletonBehaviour virtual, so that classes inheriting from it can also implement their own logic ontop during the Awake callback.
     
    Last edited: Feb 8, 2023
  13. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    Well, in the strict OOP sense that would be true, though it's not necessary for MonoBehaviours in particular because a derived class can simply re-implement an Awake method because Unity will always call the Awake method on the actual type since it more or less uses reflection to call the method from the C++ side. It does not use a base-class reference like you would in usual OOP. Virtual methods are slightly more expensive, though I usually do implement them as virtual. It just looks cleaner :)

    What you generally should do is implement all the Unity callbacks as "protected". You don't want an external class to call any of those methods explicitly, however you may need to call them from a derived class when you override / hide them in the derived class.
     
  14. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,114
    Or in the case of singletons, another option would be to load the instance lazily when the Instance is requested for the first time, avoiding the need for using the Awake function at all.
    Code (CSharp):
    1. [RequireComponent(typeof(DontDestroyOnLoad))]
    2. public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
    3. {
    4.     private static T instance;
    5.     public static T Instance => instance ??= Instantiate(Resources.Load<T>("Singletons/" + typeof(T).Name));
    6. }
    7.  
    8. internal sealed class DontDestroyOnLoad : MonoBehaviour
    9. {
    10.     private void Awake()
    11.     {
    12.         DontDestroyOnLoad(gameObject);
    13.         Destroy(this);
    14.     }
    15. }
    This also has the benefit of not having to worry about script execution orders, as instances get loaded exactly right before they are needed for the first time.

    One downside with this approach though is that dragging references between singletons and scene objects is no longer possible (though there are various workarounds for this limitation).
     
    Last edited: Feb 8, 2023
  15. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    CAREFUL! There be dragons here. Hiding this type of stuff in a "Singleton" base class leads to all kinds of misunderstandings, as the lifetime of these things isn't quite always the same, or changes as you develop.

    Not only that but you should NEVER EVER EVER drop a long-lifetime object into a scene. Why? Because invariably as it grows it holds a reference to something shorter lived and then blows up when you load scenes.

    Instead, make all your singletons and managers NOT require being dropped into scenes. If they require configuration via editor, put them in prefabs (or ScriptableObjects) that get loaded, yes, but never in prefabs that exist in scene. And make it nice and pure and simple, like so:

    Simple Singleton (UnitySingleton):

    Some super-simple Singleton examples to take and modify:

    Simple Unity3D Singleton (no predefined data):

    https://gist.github.com/kurtdekker/775bb97614047072f7004d6fb9ccce30

    Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

    https://gist.github.com/kurtdekker/2f07be6f6a844cf82110fc42a774a625

    These are pure-code solutions, DO NOT put anything into any scene, just access it via .Instance!

    The above solutions can be modified to additively load a scene instead, BUT scenes do not load until end of frame, which means your static factory cannot return the instance that will be in the to-be-loaded scene. This is a minor limitation that is simple to work around.

    If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

    Code (csharp):
    1. public void DestroyThyself()
    2. {
    3.    Destroy(gameObject);
    4.    Instance = null;    // because destroy doesn't happen until end of frame
    5. }
    There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

    OR just make a custom ScriptableObject that has the shared fields you want for the duration of many scenes, and drag references to that one ScriptableObject instance into everything that needs it. It scales up to a certain point.

    If you really insist on a barebones C# singleton, here's a highlander (there can only be one):

    https://gist.github.com/kurtdekker/b860fe6734583f8dc70eec475b1e7163

    And finally there's always just a simple "static locator" pattern you can use on MonoBehaviour-derived classes, just to give global access to them during their lifecycle.

    WARNING: this does NOT control their uniqueness.

    WARNING: this does NOT control their lifecycle.

    Code (csharp):
    1. public static MyClass Instance { get; private set; }
    2.  
    3. void OnEnable()
    4. {
    5.   Instance = this;
    6. }
    7. void OnDisable()
    8. {
    9.   Instance = null;     // keep everybody honest when we're not around
    10. }
    Anyone can get at it via
    MyClass.Instance.
    , but only while it exists.
     
  16. SF_FrankvHoof

    SF_FrankvHoof

    Joined:
    Apr 1, 2022
    Posts:
    780
    Since everyone is necroing this thread to throw in their Singleton-classes.. Here's mine:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. /// <summary>
    4. /// Behaviours are Scripts that use Unity-Methods (e.g. Update)
    5. /// to perform basic functionality on a GameObject
    6. /// </summary>
    7. namespace Utils.Behaviours
    8. {
    9.     /// <summary>
    10.     /// SingletonObject. Allows only 1 instance to exists. Calls DontDestroyOnLoad on (root-)GameObject to prevent destruction when switching Scenes
    11.     /// </summary>
    12.     /// <typeparam name="T">Type for Object</typeparam>
    13.     public abstract class SingletonBehaviour<T> : MonoBehaviour where T : MonoBehaviour
    14.     {
    15.         #region Properties
    16.         /// <summary>
    17.         /// Whether an Instance has currently been loaded (Instance != null)
    18.         /// </summary>
    19.         public static bool Exists => instance != null;
    20.  
    21.         /// <summary>
    22.         /// Singleton-Instance for Object. Auto-Created if currently null
    23.         /// </summary>
    24.         public static T Instance
    25.         {
    26.             get
    27.             {
    28.                 if (instance != null)
    29.                     return instance;
    30.                 GameObject obj = new GameObject
    31.                 {
    32.                     name = typeof(T).Name + "-Singleton"
    33.                 };
    34.                 Debug.LogWarningFormat(obj, "Singleton-Instance for {0} does not exist. Created", typeof(T).Name);
    35.                 instance = obj.AddComponent<T>();
    36.                 return instance;
    37.             }
    38.             protected set { instance = value; }
    39.         }
    40.  
    41.         /// <summary>
    42.         /// Internal Singleton-Reference
    43.         /// </summary>
    44.         protected static T instance;
    45.  
    46.         /// <summary>
    47.         /// Whether this Singleton has a Root-Object. If true, root-Object will be added to DontDestroyOnLoad instead
    48.         /// </summary>
    49.         [SerializeField]
    50.         [Tooltip("Whether this Singleton has a Root-Object. If true, root-Object will be added to DontDestroyOnLoad instead")]
    51.         protected bool hasRootObject;
    52.         #endregion
    53.  
    54.         #region Methods
    55.         /// <summary>
    56.         /// Singleton-Setup. Remember to call base.Awake() if overriding
    57.         /// </summary>
    58.         protected virtual void Awake()
    59.         {
    60.             if (Exists && instance != this)
    61.             {
    62.                 Debug.LogErrorFormat(instance.gameObject, "Singleton<{0}> already exists! Existing Object: {1}. Destroying new object {2}", typeof(T).Name, instance.gameObject.name, gameObject.name);
    63.                 Destroy(gameObject);
    64.                 return;
    65.             }
    66.             if (!hasRootObject && transform.parent != null)
    67.                 Debug.LogWarningFormat(gameObject, "Singleton<{0}> on {1} is not a root-object. Did you mean to set HasRootObject?", typeof(T).Name, gameObject.name);
    68.             if (!hasRootObject)
    69.                 DontDestroyOnLoad(gameObject);
    70.             else
    71.                 DontDestroyOnLoad(transform.root.gameObject);
    72.             instance = this as T;
    73.         }
    74.  
    75.         /// <summary>
    76.         /// Singleton-Destruction
    77.         /// </summary>
    78.         protected virtual void OnDestroy()
    79.         {
    80.             if (Exists && instance == this)
    81.                 instance = null;
    82.         }
    83.         #endregion
    84.     }
    85. }
    I usually use both this script, as well as "CachedBehaviour<T>"
    Where CachedBehaviour is different in a few ways:
    1. No DontDestroyOnLoad, so the Object is Loaded & Unloaded with its Scene
    2. No Lazy-Instantiation (a CachedBehaviour is NOT automatically created upon a get of Instance if it does not exist, but will simply return NULL).
     
    Last edited: Feb 9, 2023
    SisusCo and Kurt-Dekker like this.
  17. y0_0go

    y0_0go

    Joined:
    Aug 17, 2022
    Posts:
    2
    Why can't we use generic as a script component on a gameObject ? I also tried and failed. But can't understand why.
     
  18. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,842
    Because generics are a compile time only thing. They are in effect 'incomplete', the similar in a way to an abstract class represents an incomplete definition (big simplification).

    You always need a concrete, non-abstract class for your components.
     
    Bunny83 likes this.
  19. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    83
    I also found this singleton pattern within one of the Unity example projects (I believe it was the 2D-Platformer template), which is an interesting take on how singletons are managed:
    Code (CSharp):
    1.     /// <summary>
    2.     /// This class acts as a way for creating and accessing singleton references for any class.
    3.     /// </summary>
    4.     public static class Singletons
    5.     {
    6.         /// <summary>
    7.         /// This class provides a container for creating singletons for any class.
    8.         /// </summary>
    9.         static class InstanceRegister<T> where T : class, new()
    10.         {
    11.             public static T instance = new T();
    12.         }
    13.  
    14.         /// <summary>
    15.         /// Return the singleton instance for a class.
    16.         /// </summary>
    17.         static public T GetInstance<T>() where T : class, new()
    18.         {
    19.             return InstanceRegister<T>.instance;
    20.         }
    21.  
    22.         /// <summary>
    23.         /// Set the singleton instance for a class.
    24.         /// </summary>
    25.         static public void SetInstance<T>(T instance) where T : class, new()
    26.         {
    27.             InstanceRegister<T>.instance = instance;
    28.         }
    29.  
    30.         /// <summary>
    31.         /// Remove the static instance reference for a class.
    32.         /// </summary>
    33.         static public void DestroyInstance<T>() where T : class, new()
    34.         {
    35.             InstanceRegister<T>.instance = null;
    36.         }
    37.     }

    It can be used for any type of class, but MonoBehaviours for I created this little component that can turn another MonoBehaviour into a Singleton:
    Code (CSharp):
    1.     /// <summary>
    2.     /// This component will make another component a singleton, by creating a static reference through the static Singletons class.
    3.     /// In order for this to work well, set this script before all other scripts within Unity's ScriptExecutionOrder,
    4.     /// so that singletons are set before any other component tries to get them during Awake.
    5.     /// </summary>
    6.     public class Singleton : MonoBehaviour
    7.     {
    8.         public MonoBehaviour ComponentToMakeSingleton;
    9.  
    10.         private void Awake()
    11.         {
    12.             // this will call the generic method Singleton.SetInstance using the concrete type of the referenced MonoBehaviour
    13.             Type componentType = ComponentToMakeSingleton.GetType();
    14.             MethodInfo setInstanceMethod = typeof(Singletons).GetMethod(nameof(Singletons.SetInstance)).MakeGenericMethod(componentType);
    15.             setInstanceMethod.Invoke(null, new object[] { ComponentToMakeSingleton });
    16.         }
    17.     }

    Getting singletons:
    Code (CSharp):
    1. MyClass mySingletonObject = Singletons.GetInstance<MyClass>();
    Code (CSharp):
    1. private MyClass _mySingletonObject = null;
    2. public MyClass MySingletonObject
    3. {
    4.     get
    5.     {
    6.         if (_mySingletonObject == null)
    7.             _mySingletonObject = Singletons.GetInstance<MyClass>();
    8.         return _mySingletonObject;
    9.     }
    10. }
     
    Last edited: Apr 21, 2023
  20. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    This is an old post, for sure ..
    .. but since this is reanimated to immortality, I wanted to add, for the record, that this kind of setup is known (in C++ circles probably) as a "curiously recurring template pattern" and I've come across and implemented multiple variants of it in the last 3-4 years. Here is one such example. It's actually pretty useful in C# and depending on how it's implemented it doesn't even have to suffer from the recursion black magic (if you study the example you can see why).
     
    SisusCo likes this.