-
-
Save kurtdekker/0da9a9721c15bd3af1d2ced0a367e24e to your computer and use it in GitHub Desktop.
using UnityEngine; | |
using System.Collections; | |
public class CallAfterDelay : MonoBehaviour | |
{ | |
float delay; | |
System.Action action; | |
// Will never call this frame, always the next frame at the earliest | |
public static CallAfterDelay Create( float delay, System.Action action) | |
{ | |
CallAfterDelay cad = new GameObject("CallAfterDelay").AddComponent<CallAfterDelay>(); | |
cad.delay = delay; | |
cad.action = action; | |
return cad; | |
} | |
float age; | |
void Update() | |
{ | |
if (age > delay) | |
{ | |
action(); | |
Destroy ( gameObject); | |
} | |
} | |
void LateUpdate() | |
{ | |
age += Time.deltaTime; | |
} | |
} |
Specific use of playing a button sound and waiting for the sound to finish, then moving on:
AudioSource.PlayOneShot( ButtonAudioClip);
CallAfterDelay.Create( ButtonAudioClip.length, () => {
UnityEngine.SceneManagement.SceneManager.LoadScene( "MyNextScene");
});
Warning: you may want to disable the button too, otherwise the user can spam the sound.
If you want a CallAfterDelay instance to die at the same time as the rest of your scene, so you don't get null references if you change scenes while one of these is pending, I find the easiest way is to parent the CallAfterDelay instance to the script where you call it from, something like this:
CallAfterDelay.Create( 2.0f, () => {
myButton.interactive = true;
}).transform.SetParent( myButton.transform);
That will set myButton to interactive in 2 seconds, but if you change scenes (or destroy myButton) before then, it avoids firing a pesky missing / destroyed reference.
@kurtdekker I'm curious why you changed from coroutine to the current method?
@omundy @kurtdekker I'm also wondering the same thing. You can have the same behaviour with Coroutines. Callbacks from Update + LateUpdate might be slower too compared to the pure boolean comparison of Coroutine in Update (I suppose it's running in Update, although I'm not sure).
why not coroutine ?
I have seen this person comment on coroutine functionality and use cases on several different threads, and this is one of many examples of alternatives to coroutines that they have made available. They preach caution around using coroutines and I have gone back and forth on their philosophy for years now. I'm sure I can't put it as eloquently (or informatively) as they can, but my understanding is that coroutines, while convenient, can quickly become unmanageable if relied on too heavily and they can introduce bugs if used in situations more complex than the simplest "set it and forget it" scenarios.
If your project is something extremely small or is a prototype, then I'd go with coroutines to get it working, but if you want scalability and precise execution control, you should probably take the time to implement something like this in most cases.
A whole coroutine brain dump of theirs that I just came from:
https://discussions.unity.com/t/coroutine-for-heavy-operation/886052/2
Use CallAfterDelay to cause something to happen later in Unity3D.
For instance:
CallAfterDelay.Create( 2.0f, () => {
Debug.Log( "This is two seconds later.");
});
To make it survive from scene to scene, simply set the returned reference as DontDestroyOnLoad(), like so:
DontDestroyOnLoad( CallAfterDelay.Create( 2.0f, () => {
Debug.Log( "This is two seconds later and survives scene changes.");
}));