Last active
August 31, 2023 10:19
-
-
Save MattOstgard/6e385f61b9b04f987cfd934c9413899e to your computer and use it in GitHub Desktop.
Helper for determining what order the Unity Editor makes callbacks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==== UnityCallbackCheatSheetMonoBehaviour.cs ==== | |
// | |
// Use this to determine what order the editor makes callbacks. | |
// | |
// Extra useful note: Use `UnityEditor.SessionState.SetString` and the other type variants to save a value for only the | |
// current editor session. The value will be erased after exiting. | |
// | |
// Below is the order I found during execution in the editor. Process was I would open the editor, enter play mode, | |
// then exit play mode. | |
// | |
// max=0: UnityEditor.InitializeOnLoadAttribute called class static constructor. | |
// max=1: Constructor called when creating an instance through a static constructor. | |
// max=2: UnityEditor.InitializeOnEnterPlayModeAttribute. | |
// max=3: UnityEditor.InitializeOnLoadMethodAttribute. | |
// max=4: UnityEditor.Callbacks.DidReloadScriptsAttribute. | |
// max=5: MonoBehaviour UnityEngine.RuntimeInitializeOnLoadMethodAttribute with parameter RuntimeInitializeLoadType.SubsystemRegistration. | |
// max=6: MonoBehaviour Awake. | |
// max=7: MonoBehaviour OnEnable. | |
// max=8: UnityEngine.RuntimeInitializeOnLoadMethodAttribute with parameter RuntimeInitializeLoadType.SubsystemRegistration. | |
// max=9: UnityEngine.RuntimeInitializeOnLoadMethodAttribute with parameter RuntimeInitializeLoadType.AfterAssembliesLoaded. | |
// max=10: UnityEngine.RuntimeInitializeOnLoadMethodAttribute with parameter RuntimeInitializeLoadType.BeforeSplashScreen. | |
// max=11: UnityEngine.RuntimeInitializeOnLoadMethodAttribute with parameter RuntimeInitializeLoadType.BeforeSceneLoad. | |
// max=12: UnityEngine.RuntimeInitializeOnLoadMethodAttribute with no parameters. | |
// max=13: UnityEngine.RuntimeInitializeOnLoadMethodAttribute with parameter RuntimeInitializeLoadType.AfterSceneLoad. | |
// max=14: MonoBehaviour Start. | |
// max=15: MonoBehaviour Update. | |
// max=16: UnityEditor.EditorApplication.playModeStateChanged changed to EnteredPlayMode (first call). | |
// max=18: UnityEditor.EditorApplication.playModeStateChanged changed to ExitingPlayMode (first call). | |
// max=19: Application.quitting. | |
// max=20: MonoBehaviour OnDisable. | |
// max=21: MonoBehaviour OnDestroy. | |
// max=21: UnityEditor.SceneView.beforeSceneGui (first call, ignoring future calls). | |
// max=22: UnityEditor.EditorApplication.playModeStateChanged changed to EnteredEditMode (first call). | |
// max=23: UnityEditor.EditorApplication.playModeStateChanged changed to ExitingEditMode (first call). | |
// max=24: Destructor. | |
#if UNITY_EDITOR | |
using UnityEngine; | |
public class UnityCallbackCheatSheetMonoBehaviour : MonoBehaviour { | |
private static CallbackCheatSheetMonoBehaviour instance; | |
private static bool hasInstance = false; | |
private static bool wasUpdateCalled = false; | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] | |
private static void OnRuntimeInitializeOnLoadMethod_SubsystemRegistration() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={CallbackCheatSheet.callOrder++}, max=5) MonoBehaviour " | |
+ "`UnityEngine.RuntimeInitializeOnLoadMethodAttribute` with parameter " | |
+ "`RuntimeInitializeLoadType.SubsystemRegistration`." | |
); | |
if (instance != null) return; | |
var gObj = new GameObject(nameof(CallbackCheatSheetMonoBehaviour)); | |
instance = gObj.AddComponent<CallbackCheatSheetMonoBehaviour>(); | |
hasInstance = true; | |
} | |
protected void Awake() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={CallbackCheatSheet.callOrder++}, max=6) MonoBehaviour " | |
+ "`Awake`." | |
); | |
if (hasInstance) { | |
Destroy(gameObject); | |
} | |
} | |
protected void OnEnable() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={CallbackCheatSheet.callOrder++}, max=7) MonoBehaviour " | |
+ "`OnEnable`." | |
); | |
} | |
protected void Start() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={CallbackCheatSheet.callOrder++}, max=14) MonoBehaviour " | |
+ "`Start`." | |
); | |
} | |
protected void Update() { | |
if (wasUpdateCalled) return; | |
wasUpdateCalled = true; | |
// ReSharper disable once Unity.PerformanceCriticalCodeInvocation | |
Debug.Log( | |
$"CallbackCheatSheet: (order={CallbackCheatSheet.callOrder++}, max=15) MonoBehaviour " | |
+ "`Update`." | |
); | |
} | |
protected void OnDisable() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={CallbackCheatSheet.callOrder++}, max=20) MonoBehaviour " | |
+ "`OnDisable`." | |
); | |
} | |
protected void OnDestroy() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={CallbackCheatSheet.callOrder++}, max=21) MonoBehaviour " | |
+ "`OnDestroy`." | |
); | |
instance = null; | |
hasInstance = false; | |
} | |
} | |
[UnityEditor.InitializeOnLoad] | |
public class CallbackCheatSheet { | |
private static CallbackCheatSheet instance; | |
public static int callOrder = 0; | |
private static bool wasOnBeforeSceneGuiCalled = false; | |
private static bool wasPlayModeStateChanged_EnteredEditMode = false; | |
private static bool wasPlayModeStateChanged_EnteredPlayMode = false; | |
private static bool wasPlayModeStateChanged_ExitingEditMode = false; | |
private static bool wasPlayModeStateChanged_ExitingPlayMode = false; | |
/// <summary> Called at start of editor playmode runtime only if "Edit > Project Settings > Editor > Enter Play | |
/// Mode Settings > Reload Domain" is on. </summary> | |
static CallbackCheatSheet() { | |
callOrder = 0; | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=0) " | |
+ "`UnityEditor.InitializeOnLoadAttribute` called class static constructor." | |
); | |
instance = new CallbackCheatSheet(); | |
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += OnEditorSceneManagerSceneOpened; | |
UnityEditor.SceneView.beforeSceneGui += OnBeforeSceneGui; | |
UnityEditor.EditorApplication.playModeStateChanged += OnPlayModeStateChanged; | |
Application.quitting += OnApplicationQuitting; | |
} | |
private static void OnPlayModeStateChanged(UnityEditor.PlayModeStateChange playModeStateChange) { | |
switch (playModeStateChange) { | |
case UnityEditor.PlayModeStateChange.EnteredEditMode: | |
if (!wasPlayModeStateChanged_EnteredEditMode) { | |
wasPlayModeStateChanged_EnteredEditMode = true; | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=22) " | |
+ "`UnityEditor.EditorApplication.playModeStateChanged` " | |
+ "changed to EnteredEditMode (first call, ignoring future calls)." | |
); | |
} | |
break; | |
case UnityEditor.PlayModeStateChange.EnteredPlayMode: | |
if (!wasPlayModeStateChanged_EnteredPlayMode) { | |
wasPlayModeStateChanged_EnteredPlayMode = true; | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=16) " | |
+ "`UnityEditor.EditorApplication.playModeStateChanged` " | |
+ "changed to EnteredPlayMode (first call, ignoring future calls)." | |
); | |
} | |
break; | |
case UnityEditor.PlayModeStateChange.ExitingEditMode: | |
if (!wasPlayModeStateChanged_ExitingEditMode) { | |
wasPlayModeStateChanged_ExitingEditMode = true; | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=23) " | |
+ "`UnityEditor.EditorApplication.playModeStateChanged` " | |
+ "changed to ExitingEditMode (first call, ignoring future calls)." | |
); | |
} | |
break; | |
case UnityEditor.PlayModeStateChange.ExitingPlayMode: | |
if (!wasPlayModeStateChanged_ExitingPlayMode) { | |
wasPlayModeStateChanged_ExitingPlayMode = true; | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=18) " | |
+ "`UnityEditor.EditorApplication.playModeStateChanged` " | |
+ "changed to ExitingPlayMode (first call, ignoring future calls)." | |
); | |
} | |
break; | |
default: | |
Debug.LogError($"Unexpected PlayModeStateChange: {playModeStateChange}"); | |
break; | |
} | |
} | |
private static void OnApplicationQuitting() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=19) " | |
+ "`Application.quitting`." | |
); | |
} | |
CallbackCheatSheet() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=1) " | |
+ "Constructor called when creating instance through static constructor." | |
); | |
} | |
/// <summary> Called at edit-time or runtime regardless if "Edit > Project Settings > Editor > Enter Play Mode | |
/// Settings > Reload Domain" or "Reload Scene" is on. | |
/// https://docs.unity3d.com/ScriptReference/InitializeOnEnterPlayModeAttribute.html </summary> | |
[UnityEditor.InitializeOnEnterPlayMode] | |
private static void OnEditorInitializeOnEnterPlayMode() { | |
bool isDomainReloadOn = !( | |
UnityEditor.EditorSettings.enterPlayModeOptionsEnabled | |
&& UnityEditor.EditorSettings.enterPlayModeOptions.HasFlag( | |
UnityEditor.EnterPlayModeOptions.DisableDomainReload | |
) | |
); | |
bool isSceneReloadOn = !( | |
UnityEditor.EditorSettings.enterPlayModeOptionsEnabled | |
&& UnityEditor.EditorSettings.enterPlayModeOptions.HasFlag( | |
UnityEditor.EnterPlayModeOptions.DisableSceneReload | |
) | |
); | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=2) " | |
+ "`UnityEditor.InitializeOnEnterPlayModeAttribute`. " | |
+ $"(isDomainReloadOn={isDomainReloadOn}, isSceneReloadOn={isSceneReloadOn})" | |
); | |
} | |
[UnityEditor.InitializeOnLoadMethod] | |
private static void OnEditorInitializeOnLoadMethod() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=3) " | |
+ "`UnityEditor.InitializeOnLoadMethodAttribute`." | |
); | |
} | |
[UnityEditor.Callbacks.DidReloadScripts] | |
private static void OnEditorDidReloadScripts() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=4) " | |
+ "`UnityEditor.Callbacks.DidReloadScriptsAttribute`." | |
); | |
} | |
/// <summary> Called at runtime regardless if "Edit > Project Settings > Editor > Enter Play Mode Settings > | |
/// Reload Domain" or "Reload Scene" is on. </summary> | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] | |
private static void OnRuntimeInitializeOnLoadMethod_SubsystemRegistration() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=8) " | |
+ "`UnityEngine.RuntimeInitializeOnLoadMethodAttribute` with parameter " | |
+ "`RuntimeInitializeLoadType.SubsystemRegistration`." | |
); | |
} | |
/// <summary> Called at runtime regardless if "Edit > Project Settings > Editor > Enter Play Mode Settings > | |
/// Reload Domain" or "Reload Scene" is on. </summary> | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)] | |
private static void OnRuntimeInitializeOnLoadMethod_AfterAssembliesLoaded() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=9) " | |
+ "`UnityEngine.RuntimeInitializeOnLoadMethodAttribute` with parameter " | |
+ "`RuntimeInitializeLoadType.AfterAssembliesLoaded`." | |
); | |
} | |
/// <summary> Called at runtime regardless if "Edit > Project Settings > Editor > Enter Play Mode Settings > | |
/// Reload Domain" or "Reload Scene" is on. </summary> | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)] | |
private static void OnRuntimeInitializeOnLoadMethod_BeforeSplashScreen() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=10) " | |
+ "`UnityEngine.RuntimeInitializeOnLoadMethodAttribute` with parameter " | |
+ "`RuntimeInitializeLoadType.BeforeSplashScreen`." | |
); | |
} | |
/// <summary> Called at runtime regardless if "Edit > Project Settings > Editor > Enter Play Mode Settings > | |
/// Reload Domain" or "Reload Scene" is on. </summary> | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] | |
private static void OnRuntimeInitializeOnLoadMethod_BeforeSceneLoad() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=11) " | |
+ "`UnityEngine.RuntimeInitializeOnLoadMethodAttribute` with parameter " | |
+ "`RuntimeInitializeLoadType.BeforeSceneLoad`." | |
); | |
} | |
/// <summary> Called at runtime regardless if "Edit > Project Settings > Editor > Enter Play Mode Settings > | |
/// Reload Domain" or "Reload Scene" is on. </summary> | |
[RuntimeInitializeOnLoadMethod] | |
private static void OnRuntimeInitializeOnLoadMethod() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=12) " | |
+ "`UnityEngine.RuntimeInitializeOnLoadMethodAttribute` with no parameters." | |
); | |
} | |
/// <summary> Called at runtime regardless if "Edit > Project Settings > Editor > Enter Play Mode Settings > | |
/// Reload Domain" or "Reload Scene" is on. </summary> | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] | |
private static void OnRuntimeInitializeOnLoadMethod_AfterSceneLoad() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=13) " | |
+ "`UnityEngine.RuntimeInitializeOnLoadMethodAttribute` with parameter " | |
+ "`RuntimeInitializeLoadType.AfterSceneLoad`." | |
); | |
} | |
private static void OnBeforeSceneGui(UnityEditor.SceneView sceneView) { | |
if (wasOnBeforeSceneGuiCalled) return; | |
wasOnBeforeSceneGuiCalled = true; | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=21) " | |
+ "`UnityEditor.SceneView.beforeSceneGui` (first call, ignoring future calls)." | |
); | |
} | |
private static void OnEditorSceneManagerSceneOpened( | |
UnityEngine.SceneManagement.Scene scene, UnityEditor.SceneManagement.OpenSceneMode openSceneMode | |
) { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=?) " | |
+ "`UnityEditor.SceneManagement.EditorSceneManager.sceneOpened`." | |
); | |
} | |
~CallbackCheatSheet() { | |
Debug.Log( | |
$"CallbackCheatSheet: (order={callOrder++}, max=24) " | |
+ "Destructor." | |
); | |
if (instance == this) { | |
instance = null; | |
} | |
} | |
} | |
#endif // UNITY_EDITOR |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment