and for some reason it throws me a Nullreference error for each object containing the ObjBehaviour class, specifically for the subscription and desubscription of OnSpecialStart.
The weird part is: If I declare a “public SpecialMove spMove” and then insert the SpecialMove script accordingly in the inspector, and subscribe/desubscribe via “spMove.SpecialStart += OnSpecialStart” I do NOT get an error. Also, I have another class (which is also a singleton), lets call it “class C” subscribe/unsubscribe to the events by calling “SpecialMove.instance.SpecialStart += OnSpecialStart”, and in that case I do not get the error.
What could be the reason for this? Other than that the class ObjBehavior is a component of multiple different gameobjects, there is no difference to the event subscription compared to class C, where it does work.
Im away from my pc for the good part of the day, but will check whether I have declared the methods and Awake function etc correctly later tonight.
Regarding the purpose of the static MonoBehaviour:
The SpecialMove class checks certain button inputs and conditions and starts a “special move” (particle effects, time slowmotion, etc) if all the requirements are met. Im creating the two event actions SpecialStart and SpecialEnd so that other scripts can be notified when the special move is active and how long it lasts.
I am still relatively new to game development so there might be a better solution for this.
Again I will check later tonight if there really are no mistakes in my code, I wrote it very late last night and got frustrated after 2 hours or so of not finding the mistake.
It’s weird and I wouldn’t recommend. It doesn’t have to… but it probably smells.
don’t do these things to MonoBehaviours. treat them as extensions to the underlying system.
don’t inherit them, promote them to singletons, pass them around as static references, expect them to persist and not die etc
you can do all of that to normal C# objects, MonoBehaviours are volatile, because what you see is just a wrapper, a tip of an iceberg.
, but again, in another script (class C), the subscription to the event via “SpecialMove.instance.SpecialStart += OnSpecialStart” works without a problem.
Can you elaborate on that? As I said Im still new and would appreciate if someone could tell the correct usage for MonoBehaviours, I might be doing things wrong.
I only promote MonoBehaviours to singeltons, if they have some kind of “managing” role, as in AudioManager, GameManger etc, and put them on empty GameObjects which I call the same.
My reason for having the singleton SpecialMove inherit from MonoBehaviour, is because it needs to be checked every frame (in update), and to my knowledege that is the only possible way.
Also to elaborate a little bit why I don’t manually link the SpecialMove class to the ObjBehaviour class on my gameobjects in the inspector: I am instantiating many game objects that have the ObjBehaviour script as a component, so they need to be able to subscribe (and find) my SpecialMove class in runtime, once they are initiated.
When starting the level I have 8 game objects who have the ObjBehaviour class with the above lines on it.
For some reason I get 8 prints of “A”, but only one print of “B”, meaning that the code only made it successfully through OnEnable() once, and failed 7 times.
I really suspect that the problem is somehow related to the many instances of ObjBehaviour I have, but I dont understand why.
Sure, here’s another recent thread where I did elaborate a bit. I wrote a proper singleton example, and a phat comment on MonoBehaviours in the end. https://discussions.unity.com/t/778842
You basically only need to decouple your critical path from relying onto MonoBehaviours as if they’re a system foundation. You should mostly treat them only as an entry point, a gateway system to cross-communicate your data. A ghastly apparition. There is no need to interlock design patterns with them, because they can be reserialized or turn dead without warnings or guarantees. You can see in the thread that this person had an issue with a singleton where it couldn’t persist, which violates the basic assumptions for a singleton. Why would you entangle yourself with such unnecessary lack of persistence control…
Now don’t get me wrong, there are ways to establish control, there are ways to sniff the potential changes before they arrive, but it’s also extra work for something that’s just as easily solved if only you stop working on top of them, and let them be.
I’m sure people have varying experiences, and make towers of Babylon with them, but in all truth, you want them to be the simplest of drivers and to easily hook to system events, only to propagate that knowledge further down the line, depending on how complex your codebase turns out to be.
And, to be clear, what you’re doing is not wrong per se, it’s just unreliable. You have almost no control. Maybe I’m overly superstitious, but in ten years I had never encountered a problem with them, simply because I’ve evaded any potential smell by top down design.
This looks like an order of operations issue. The singleton class isn’t calling Awake early enough to assign itself as the static instance. Try setting the ScriptExectionOrder of the singleton class to be lower than that of the subscribing classes.
@orionsyndrome Thank you for the detailed explanation and examples. My take away is to not make MonoBehaviours into “fake” singletons, but rather have a C# script be a true singelton, and then initialize it in a different MonoBehaviour script which is, for example, attached to some Manager Object.
@kru I might be, but then again, other scripts which have the exact same code when it comes to the event subscriptions do not have any problems with the order of operation.
I ditched the whole thing for now since I don’t seem to find a solution to the problem and found a different workaround, that doesnt involve the subscription to SpecialMove.cs’s events, but is (in my opinion) uglier.
I learned a couple of things in this thread though, so thanks for that. Maybe Ill rewrite a couple of my scripts and then try to attack events and subscriptions again.
For observers’ benefit, this is because of the order in which your scripts are executing. By default, all user created monobehaviors will have their awake/start/update/etc methods called in an indeterminate order. This means that scripts will have their methods called unpredictably.
In your case, you have some scripts trying to call SpecialMove.instance.whatever, before the SpecialMove singleton has had its Awake method called. These scripts which are being run before SpecialMove see that SpecialMove.instance is null, and throw a NullReference error.
The other scripts, which you say work fine, are being run after SpecialMove has had its Awake called, so SpecialMove.instance has been set, and everything is fine.
Go to Player Settings->Script Execution Order, drag in SpecialMove, and set its value to something negative so it runs before the other scripts. Your issue will clear up.