- Home /
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:
You cannot create static classes derived from neither
MonoBehaviour
norScriptableObject
.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:
change the base class in the above script to
MonoBehaviour
Add the script to a
GameObject
in the scene.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:
They need to be instance methods.
There needs to be an instance attached to a
GameObject
COROLLARY OF SORTS: Actually the messages are not exclusive to
MonoBehaviour
My 2 questions are:
Are the above requirements / facts indeed true?
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.
Answer by GameVortex · Oct 29, 2014 at 07:47 AM
The requirements are true. Messages are only sent to MonoBehaviours attached to GameObjects.
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.
I guess it is an option yes. And what about the fact that I was still able to get a ScriptableObject attached to a GameObject and receiving messages?
Well, messages are called through reflection, and because the Object was still attached to the GameObject I guess it was still able to find the function to call it. It does not change the fact that messages can only be sent to objects attached to a GameObject and having the ScriptableObject attached to the GameObject is not really a viable solution in the long term and will most likely lead to unwanted behavior at some point.
Your answer
Follow this Question
Related Questions
How do I detect that MonoBehaviour is modified in the Editor? 2 Answers
ScriptableObject stored in MonoBehaviour lost on quit 1 Answer
Circular Dependency Issue 0 Answers
Unet CCU limit hit callback 0 Answers