calling Unity messages without attaching script to GameObject

My empirical tests seem to imply this is not possible. But just in case, I figured I’d ask:

The Unity documentation for MonoBehaviour exposes several “messages”. They call them messages anyway. As fas as I can tell (I’m on C#) they are really neither delegates nor events nor overrides, but rather, arbitrary agreed methods which if you implement in a MonoBehaviour, the runtime knows can be called probably thru reflection.

So for instance, I’m trying to create a wrapper network services class around Unity’s Netowrk class. And since my class needs to be available between scenes and there is only ever going to be one state, I wanted to go for either just class methods or a singleton.

My first test was with static classes and concrete classes. And my findings where:

  1. You cannot create static classes derived from neither MonoBehaviour nor ScriptableObject.

  2. You can create concrete classes derived from these, but the messages need to be instance methods; they cannot be class methods. So for instance this wouldn’t work:

    public class NetworkServices : MonoBehaviour {

    public static void Init() {
    Debug.Log(“Init”);
    Debug.Log(Network.InitializeServer(2, 2500, !Network.HavePublicAddress()));
    }

    private static void OnServerInitialized() {
    Debug.Log(“server initialized”);
    }

    }

But this would:

 public class NetworkServices : MonoBehaviour {

  public static void Init() {
    Debug.Log("Init");
    Debug.Log(Network.InitializeServer(2, 2500, !Network.HavePublicAddress()));
  }

  private void OnServerInitialized() {
    Debug.Log("server initialized");
  }

}

Which seems to confirm that the ‘message’ methods need to be instance methods. So I need an instance. Ok. Now, if I had a MonoBehaviour attached to a game object, the above code (second example) would work, because attaching the Script to a GameObject implies instantiating it. But the truth is that I don’t need to create instances since I need to always access the same one. And neither do I want to go about attaching one of these scripts to some object on every scene to have it instantiate. So I tried the following:

public class NetworkServices : ScriptableObject {

  private static NetworkServices _SharedInstance;

  public static void Init() {
    Debug.Log("Init");
    if (_SharedInstance == null) {
      Debug.Log("creating instance");
      _SharedInstance = CreateInstance<NetworkServices>();
    }

    Debug.Log(Network.InitializeServer(2, 2500, !Network.HavePublicAddress()));
  }

  private void OnServerInitialized() {
    Debug.Log("server initialized");
  }

}

Which on the outside (public API) would look like simply class methods, but in reality is creating an instance (just because the message callback needs to be an instance method). So it’s kind of using a hidden singleton. But this does not work. I used ScriptableObject because in theory it allows me to create instances not attached to a GameObject. But OnServerInitialized is never called. However, here’s a hack that does sort of work:

In theory, you cannot attach a ScriptableObject to a GameObject. Also, in theory, OnServerInitialized is a message of MonoBehaviour and not of ScriptableObject; ScriptableObject inherits from Object and not from MonoBehaviour. But if Ido the following:

  1. change the base class in the above script to MonoBehaviour
  2. Add the script to a GameObject in the scene.
  3. change the base class back to ScriptableObject

I end up with a ScriptableObject attached to a GameObject and the the message gets called.

So from the above it seems that the requirements for the Unity messages to trigger are:

  1. They need to be instance methods.
  2. There needs to be an instance attached to a GameObject
  3. COROLLARY OF SORTS: Actually the messages are not exclusive to MonoBehaviour

My 2 questions are:

  1. Are the above requirements / facts indeed true?
  2. Is there a way around requirement 2? Basically I want to get the networking messages / callbacks but I don’t want to attach a script to an object on each scene just to access the singleton / class.
  1. The requirements are true. Messages are only sent to MonoBehaviours attached to GameObjects.

  2. Why not generating the gameobject at runtime and have it set to not destroy on load? Then you will not have to add it to every scene and it will persist during your entire game.

Example:

public class NetworkServices : MonoBehaviour
{
    private static NetworkServices instance;
    public static NetworkServices Instance
    {
        get
        {
            if (!instance)
            {
                instance = new GameObject("NetworkServices").AddComponent<NetworkServices>();
                DontDestroyOnLoad(instance.gameObject);
            }
            return instance;
        }
    }
}

It is then a singleton and it supports messages.