I register events in OnEnable and unregister them in OnDisable. In most cases it works but I have one simple script which is causing the MissingReferenceException: The object of type 'GraphicsSwitch' has been destroyed but you are still trying to access it. when I load a new scene and invoke the event by pressing a button.
I know that the OnDisable is called as I checked with Debug.Log. The event is not invoked in the new scene before I press a button in the new scene.
After adding if (this != null) the error is gone. Checking if the gameObject is null is not working. It seems that the event is not unsubscribed and the method is called on destroyed object from the previous scene.
The code is very simple:
private void OnEnable()
{
AdjustGraphics();
GameSettings.GraphicsChanged += AdjustGraphics;
}
private void OnDisable()
{
GameSettings.GraphicsChanged -= AdjustGraphics;
}
private void AdjustGraphics()
{
//without this line I get the error
if (this != null)
gameObject.SetActive(GameSettings.Graphics >= requiredQuality);
}
Method AdjustGraphics should not be called in the new loaded scene. I thought the objects should be unsubscribed without any delay. Is it something I am missing when it comes to unsubscribing events?
I know objects in Unity are not destroyed right away but the OnDisable is called on time so my script/object should not listen to GameSettings.GraphicsChanged event anymore when new scene is loaded.
This link helped me:
https://forum.unity.com/threads/how-to-remove-and-inputaction-phase-callback.894601/
private void doSomething(CallbackContext ctx)
{
// do the thing
}
//register
action.started += doSomething;
//unregsiter
action.started -= doSomething;
Correct me if I'm wrong but when you loading a new scene, your objects are destroyed hence the MissingReferenceException unless you use DontDestroyOnLoad.
If you don't want to use it, then make sure to initialize the object at the start of the scene load i.e. make sure your object exists in the scene before calling any of its functions.
Related
I am using Unity 3D, however, that information should be irrelevant for solving this problem as the core problem is System.Delegate (I wanted to let you know as I'll be linking to some Unity docs for clarification).
I have a custom window that has a custom update function DirectorUpdate. I need this function to run every editor update regardless of what the user/window is doing.
In order for this to be called every editor update, I Combine my method with the Delegate EditorApplication.update:
protected void OnEnable()
{
// If I do the below, base EditorApplication.update won't be called anymore.
// EditorApplication.update = this.DirectorUpdate;
// So I need to do this:
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
... // Other stuff
}
Note that this is done inside a window's OnEnable.
The problem is that OnEnable can be called more than once during a single run (for example, when closing the window and then reopening the window during a single editor session) causing
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
to be called multiple times, meaning my update method (this.DirectorUpdate) will eventually get called multiple times per update, which is causing some serious bugs.
So, the question is how do I check if EditorApplication.update already has my method "inside" of it. (By inside of it, I of course mean it has already been System.Delegate.Combine(d) to the delegate.)
I am aware that there could be other solutions, for example restoring EditorApplication.update to what it was prior when the window closes, however that won't account for all situations (for example, OnEnable also gets called during window refresh and such) and the bug will persist. (Also, what if another window Concatenates with EditorApplication.update while this window is open?) As such, the best solution would be to check if EditorApplication.update is already callin this method BEFORE Delegate.Combine-ing it.
I think you took the complicated road ;)
Subscribing and unsubscribing events and delegates is as simple as using the operators += and -= like
protected void OnEnable()
{
// You can substract your callback even though it wasn't added so far
// This makes sure it is definitely only added once (for this instance)
EditorApplication.update -= DirectorUpdate;
// This basically internally does such a Combine for you
EditorApplication.update += DirectorUpdate;
... // Other stuff
}
private void OnDisable()
{
// Remove the callback once not needed anymore
EditorApplication.update -= DirectorUpdate;
}
This way you can also have multiple instances of this window open and they all will receive the callback individually.
Btw if this is actually about an EditorWindow then afaik you should not use OnEnabled but you would rather use Awake
Called as the new window is opened.
and OnDestroy.
I am not familiar with what System.Delegate.Combine(d) does, but you can consider instead of enabling/disabling your window, destroying and instantiating it every time, and move your code to the Start or the Awake for it to be called only once per window "activation".
Last but not least, use a mighty boolean in the OnDisable so that you can handle the combine execution if your component was ever disabled. Like so:
bool omgIWasDisabled;
protected void OnEnable()
{
if (!omgIWasDisabled) {
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
}
... // Other stuff
}
void OnDisable() {
omgIWasDisabled = true;
}
Hope any of those works out.
In Unity I have a script in which I connect to a socket via TCP and want to use this connection every frame. I need to dispose and clean up after that. My idea was to use Start() to start a connection and OnDestroy()
public class Foo : MonoBehaviour
{
void Start()
{
// start TCP connection
}
void Update()
{
// use connection
}
void OnDestroy()
{
// cleanup
}
}
I need the cleanup to execute whatever happens. Does the OnDestroy() method guarantee to be called before the application stops (in standalone mode and in editor) no matter what happens to the object? If not, how can I guarantee the cleanup?
No it is not!
Even OnApplicationQuit might not get called when your app e.g. crashes for some reason.
And there are other specific cases where neither is called. I know that from my own experience that e.g. on the HoloLens2 apps are not closed but only hibernated. If you then close them via the HoloLens home "menu" then you actually kill them via task manager.
This is pretty dirty and causes neither OnDestroy nor OnApplicationQuit or any other Unity specific messages to be called and we ended up with zomby threads and still occupied TCP ports.
If you really want to go sure (e.g. for giving free the connection, killing threads etc) what I finally did was creating a dedicated class with deconstructor (Finalizer)
The deconstructor is pure c# and does not rely on Unity being shutdown correctly so it is guaranteed to be called even if the app was terminated due to a crash as soon as the Garbage Collector automatically does its job.
A MonoBehaviour itself shouldn't implement any constructor nor destructor but a private "normal" class can:
public class Foo : MonoBehaviour
{
private class FooInternal
{
private bool disposed;
public FooInternal()
{
// create TCP connection
// start thread etc
}
public void Update ()
{
// e.g. forward the Update call in order to handle received messages
// in the Unity main thread
}
public ~FooInternal()
{
Dispose();
}
public void Dispose()
{
if(disposed) return;
disposed = true;
// terminate thread, connection etc
}
}
private FooInternal _internal;
void Start()
{
_internal = new FooInternal ();
}
void Update()
{
_internal.Update();
}
void OnDestroy ()
{
_internal.Dispose();
}
}
if you never pass on the reference to _internal to anything else, the GC should automatically kill it after this instance has been destroyed as well.
You're looking for OnEnable for establishing your connection, and OnDisable for cleaning it up.
The issue with OnDestroy (and OnApplicationQuit fwiw) is it won't be called if the script is disabled, which can happen if:
You call SetActive(false) on the GameObject or any of its parents.
You set enabled = false on the script component.
You disable the GameObject or any of its parents in play mode by unchecking the box in the inspector.
You disable the script component in play mode by unchecking the box in the inspector.
The editor is about to recompile the scripts while play mode is running (an especially irritating event to miss for certain types of cleanup code [cough lookin' at you, Vuforia]).
The issue with Start is it won't be called if the script is re-enabled after having been disabled (it'll be called the first time, but no more), which happens in all of the cases opposite of the above list (as well as when the editor finishes recompilation of scripts in play mode).
On the other hand:
OnDisable will be called when the script transitions to disabled for any of the above reasons, plus all the stuff that OnDestroy and OnApplicationQuit cover.
OnEnable will be called when the script transitions to enabled for any of the above reasons, plus all the stuff that Start covers.
Additionally, you will have a 1:1 correspondence of OnEnable and OnDisable calls by Unity (of course you can call them yourself whenever you want).
You will only almost have a 1:1 correspondence of Start and OnDestroy: The notable exception is if a script component is initially disabled (i.e. you disabled it in edit mode) but its GameObject is enabled, you'll still get OnDestroy called on the script even if Start wasn't called (which may be a bug now that I think about it). Plus, of course, you will miss all the above cases.
OnEnable + OnDisable, however, handle all that cleanly, and generally do exactly what you hope that they do, in a straightforward manner with (theoretically) no weird quirks or gotchas.
For the question itself, yes, OnDestroy is reliable: It is called as documented when the script is destroyed.
Of course it wouldn't be called if the application crashes, but in general you'd have bigger problems and that's not the kind of thing you normally code around. It also probably wouldn't be called if you exploded a grenade next to the machine running the game but, again, that's all in "undefined behavior" territory.
In my GameManager script I have a delegate. In my Door script, I subscribe to this delegate. I've tried unsubscribing from it in both the OnDisable and OnDestroy methods in the Door script. In both cases, I get an error when I stop running the game in the editor:
Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)
Using Debug.Log, I found out that this is because the GameManager will always be destroyed before the Door script. Even if I do a null check inside either the OnDisable or OnDestroy of the Door script to see if the GameManager is null, I get the same error
if (GameManager.Instance)
{
GameManager.Instance.OnAllEnemiesKilled -= OpenDoor;
}
Somebody told me that I don't need to unsubscribe from it, as the delegate will automatically become null when the Door object is destroyed, but that's not true. During runtime, after the Door is destroyed, my update loop inside of the GameManager is still printing that the delegate has one subscriber: the Door.
I suspect you're spawning a new singleton when you call GameManager.Instance after it's been destroyed? If so, do something like this instead:
public class GameManager : MonoBehaviour
{
public static GameManager Instance
{
get
{
if (isDestroyed) return null;
// Your spawn logic...
}
}
static bool isDestroyed;
void OnDestroy()
{
isDestroyed = true;
}
}
As Lece pointed out, I'm spawning a new singleton when I call GameManager.Instance. Rather than creating a static bool to remedy the problem however, I replaced if (_instance == null) with if ((object)_instance == null) inside my Instance getter and it solved the problem. This is because, while the object is destroyed in the native code, it still exists in the managed code. So I'm now referencing it in the managed world. Moreover, Unity makes it so that when an object is destroyed in native code, its value in managed code will still return null.
Edit: For clarification, this is in Unity, and is tied to Update(). So the events will be triggered once per frame for any number of frames. Both events can be triggered independently by the user, but there is an overlap in which both can be triggered. In this case I only want one of the two methods that will be called by the two events to actually run.
I'm trying to make a method that is called by one event and other method that is called by a 2nd event be linked in a way that only one of those two methods can be called or ran at the same time.
The events are triggered by an action of the user, both events can be triggered at the same time. But I only want one of the two methods that would be called to actually run if both events are triggered.
I was thinking something along the like of the following example, obviously the example may not be accurate, but the gist of what I am trying to accomplish is there.
Pseudo Code Example:
public delegate void OneHandler();
public event OneHandler OneEvent;
private void One()
{
if(some requirement && TEvent != null)
{
TEvent();
}
}
public delegate void TwoHandler();
public event TwoHandler TwoEvent;
private void Two()
{
if(some requirement && TEvent != null)
{
TEvent();
}
}
SomeClass.OneEvent += ActionOne();
SomeClass.TwoEvent += ActionTwo();
private void ActionOne()
{
if(TwoEvent is not firing)
{
Do Something;
}
}
private void ActionTwo()
{
if(OneEvent is not firing)
{
Do Something;
}
}
A crude diagram. The tan area moves down/left, the green areas move diagonally. I essentially want the tan area which have their own events, to not run their methods if the mouse is within that entire area.
Events fire one at a time, not all at once. Even if the same user action was going to cause both events to fire one after the other, there is no way to know that both events will fire, or in what order they will fire.
To be more clear: I didn't mean events can never be fired concurrently. I mean that, in general, there is no piece of code that determines, for a particular situation, the complete set of events which will be fired. You can't tell ahead of time which will fire, or in which order, or even on which threads.
For example, a particular user interface action (or user gesture) might cause several events to fire. In general, you can't depend on the number of events to be fired, or the order in which they will be fired.
There are some situations, like in ASP.NET web forms, where you can be assured that if the user clicks a button which has a Click event handler, that the page Load event will fire and then that the button Click event will fire. But this is because this behavior is defined and documented that way. If the order changed, or if, for instance, the page Load event stopped firing, then this would break a large number of ASP.NET web forms applications.
If all you're trying to do is guarantee that concurrent triggering (like two threads running on different cores) will never be allowed, you'll want to use something like a mutex. You would have both handlers perform a WaitOne(0) to see if the other handler is working, returning immediately if the call returns false (since the other method must be executing) then in a guaranteed-execution block (such as a finally) you would release the mutex.
How about use bool variable as flag ?
private bool flag[2] = [false, false];
private void ActionOne()
{
flag[0] = true;
if(!(flag[0])&&flag[1]))
{
Do Something;
}
flag[0] = false;
}
private void ActionTwo()
{
flag[1] = true;
if(!(flag[0] && flag[1]))
{
Do Something;
}
flag[1] = false;
}
I'm trying to write a simple Visual Studio extension that performs an action when a file is saved:
protected override void Initialize()
{
base.Initialize();
var dte = (DTE)GetService(typeof(DTE));
dte.Events.DocumentEvents.DocumentSaved += DocumentEvents_DocumentSaved;
}
void DocumentEvents_DocumentSaved(Document doc)
{
// Do something
}
But apparently the DocumentsSaved event is never raised, so the DocumentEvents_DocumentSaved is not called...
Am I missing something? Isn't this event supposed to be raised every time a file is saved? If not, is there another way I can detect changes to the files in the solution? (I'd rather avoid resorting to a FileSystemWatcher if possible...)
(note: I know that the extension is properly loaded, since a breakpoint in the Initialize method is hit, so the problem is not there)
According to this: http://social.msdn.microsoft.com/Forums/en-US/vsx/thread/0857a868-e650-42ed-b9cc-2975dc46e994
You need to keep a strong reference to both the Events and DocumentEvents objects for it to work.