Skip to content

Commit

Permalink
Rewrote Monobehaviour logic to very closely mimic Unity's Execution O…
Browse files Browse the repository at this point in the history
…rders
  • Loading branch information
michaelsakharov committed Apr 16, 2024
1 parent 0453d82 commit 419f947
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 109 deletions.
2 changes: 1 addition & 1 deletion Prowl.Editor/Editor/CustomEditors/GameObjectEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ private static void HandleComponentContextMenu(GameObject? go, MonoBehaviour com
if (ImGui.MenuItem("Duplicate")) {
var serialized = Serializer.Serialize(comp);
var copy = Serializer.Deserialize<MonoBehaviour>(serialized);
go.AddComponentDirectly(copy);
go.AddComponent(copy);
copy.OnValidate();
}
if (ImGui.MenuItem("Delete")) toDelete.Add(comp);
Expand Down
4 changes: 0 additions & 4 deletions Prowl.Editor/PlayMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public static void Start() {

Current = Mode.Playing;
SceneManager.Clear();
MonoBehaviour.PauseLogic = false;
SceneManager.RestoreScene(); // Resets GameObjects and Components to re-trigger things like Awake() and Start()

ImGuiNotify.InsertNotification(new ImGuiToast()
Expand All @@ -25,7 +24,6 @@ public static void Start() {

public static void Pause() {
Current = Mode.Paused;
MonoBehaviour.PauseLogic = true;

ImGuiNotify.InsertNotification(new ImGuiToast()
{
Expand All @@ -35,7 +33,6 @@ public static void Pause() {

public static void Resume() {
Current = Mode.Playing;
MonoBehaviour.PauseLogic = false;

ImGuiNotify.InsertNotification(new ImGuiToast()
{
Expand All @@ -46,7 +43,6 @@ public static void Resume() {
public static void Stop() {
Current = Mode.Editing;
SceneManager.Clear();
MonoBehaviour.PauseLogic = true;

SceneManager.RestoreScene();
SceneManager.ClearStoredScene();
Expand Down
6 changes: 1 addition & 5 deletions Prowl.Editor/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ public static int Main(string[] args)
// Editor-specific initialization code
imguiController = new(GLDevice.GL, Window.InternalWindow, Input.Context);
EditorGui.Initialize();
MonoBehaviour.PauseLogic = true;
ImporterAttribute.GenerateLookUp();

// Start with the project window open
Expand Down Expand Up @@ -87,11 +86,8 @@ public static int Main(string[] args)
else if (Hotkeys.IsHotkeyDown("BuildProject", new() { Key = Key.B, Ctrl = true }))
Project.BuildProject();

Application.isPlaying = PlayMode.Current != PlayMode.Mode.Editing;
Application.isActivelyPlaying = PlayMode.Current == PlayMode.Mode.Playing;
Application.isPlaying = PlayMode.Current == PlayMode.Mode.Playing;

if (Application.isActivelyPlaying)
Physics.Update();

try
{
Expand Down
2 changes: 0 additions & 2 deletions Prowl.Runtime/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ public static class Application
{
public static bool isRunning;
public static bool isPlaying = false;
public static bool isActivelyPlaying = false;
public static bool isEditor { get; private set; }

public static string? DataPath = null;
Expand Down Expand Up @@ -69,7 +68,6 @@ public static void Run(string title, int width, int height, IAssetProvider asset

isRunning = true;
isPlaying = true; // Base application is not the editor, isplaying is always true
isActivelyPlaying = true; // Base application is not the editor, isActivelyPlaying is always true
Window.Start();
}

Expand Down
4 changes: 2 additions & 2 deletions Prowl.Runtime/Components/Camera.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ internal void RenderAllOfOrder(RenderingOrder order)

private void OpaquePass()
{
SceneManager.ForeachComponent((x) => Try(x.OnPreRender));
SceneManager.ForeachComponent((x) => x.Do(x.OnPreRender));
gBuffer.Begin(); // Start
RenderAllOfOrder(RenderingOrder.Opaque); // Render
gBuffer.End(); // End
SceneManager.ForeachComponent((x) => Try(x.OnPostRender));
SceneManager.ForeachComponent((x) => x.Do(x.OnPostRender));
}

Matrix4x4? oldView = null;
Expand Down
57 changes: 36 additions & 21 deletions Prowl.Runtime/GameObject/GameObject.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Prowl.Runtime.SceneManagement;
using Prowl.Runtime.Utils;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

Expand All @@ -11,7 +13,7 @@ namespace Prowl.Runtime;
/// The Base Class for all Object/Entities in a Scene.
/// Holds a collection of Components that contain the logic for this Object/Entity
/// </summary>
public class GameObject : EngineObject, ISerializable, ISerializationCallbackReceiver
public class GameObject : EngineObject, ISerializable
{
#region Static Fields/Properties

Expand Down Expand Up @@ -251,6 +253,21 @@ public bool IsParentOf(GameObject go)

public static GameObject[] FindGameObjectsWithTag(string otherTag) => FindObjectsOfType<GameObject>().Where(gameObject => gameObject.CompareTag(otherTag)).ToArray();

internal void PreUpdate()
{
foreach (var component in _components)
{
if (!component.HasAwoken)
component.Do(component.InternalAwake);

if (!component.HasStarted)
if (component.EnabledInHierarchy)
{
component.Do(component.InternalStart);
}
}
}

public T AddComponent<T>() where T : MonoBehaviour, new() => AddComponent(typeof(T)) as T;

public MonoBehaviour AddComponent(Type type)
Expand Down Expand Up @@ -286,12 +303,16 @@ public MonoBehaviour AddComponent(Type type)
newComponent.AttachToGameObject(this);
_components.Add(newComponent);
_componentCache.Add(type, newComponent);
newComponent.TriggerAwake();

if (enabledInHierarchy)
{
newComponent.Do(newComponent.InternalAwake);
}

return newComponent;
}

public void AddComponentDirectly(MonoBehaviour comp)
public void AddComponent(MonoBehaviour comp)
{
var type = comp.GetType();
var requireComponentAttribute = type.GetCustomAttribute<RequireComponentAttribute>();
Expand Down Expand Up @@ -320,7 +341,10 @@ public void AddComponentDirectly(MonoBehaviour comp)
comp.AttachToGameObject(this);
_components.Add(comp);
_componentCache.Add(comp.GetType(), comp);
comp.TriggerAwake();
if (enabledInHierarchy)
{
comp.Do(comp.InternalAwake);
}
}

public void RemoveAll<T>() where T : MonoBehaviour
Expand All @@ -330,11 +354,11 @@ public void RemoveAll<T>() where T : MonoBehaviour
{
foreach (MonoBehaviour c in components)
if (c.EnabledInHierarchy)
c.Internal_OnDisabled();
c.Do(c.OnDisable);
foreach (MonoBehaviour c in components)
{
if (c.HasStarted) // OnDestroy is only called if the component has previously been active
c.Internal_OnDestroy();
c.Do(c.OnDestroy);

_components.Remove(c);
}
Expand All @@ -349,8 +373,8 @@ public void RemoveComponent<T>(T component) where T : MonoBehaviour
_components.Remove(component);
_componentCache.Remove(typeof(T), component);

if (component.EnabledInHierarchy) component.Internal_OnDisabled();
if (component.HasStarted) component.Internal_OnDestroy(); // OnDestroy is only called if the component has previously been active
if (component.EnabledInHierarchy) component.Do(component.OnDisable);
if (component.HasStarted) component.Do(component.OnDestroy); // OnDestroy is only called if the component has previously been active
}

public void RemoveComponent(MonoBehaviour component)
Expand All @@ -360,8 +384,8 @@ public void RemoveComponent(MonoBehaviour component)
_components.Remove(component);
_componentCache.Remove(component.GetType(), component);

if (component.EnabledInHierarchy) component.Internal_OnDisabled();
if (component.HasStarted) component.Internal_OnDestroy(); // OnDestroy is only called if the component has previously been active
if (component.EnabledInHierarchy) component.Do(component.OnDisable);
if (component.HasStarted) component.Do(component.OnDestroy); // OnDestroy is only called if the component has previously been active
}

public T? GetComponent<T>() where T : MonoBehaviour => (T?)GetComponent(typeof(T));
Expand Down Expand Up @@ -536,8 +560,8 @@ public override void OnDispose()

foreach (var component in _components)
{
if (component.EnabledInHierarchy) component.Internal_OnDisabled();
if (component.HasStarted) component.Internal_OnDestroy(); // OnDestroy is only called if the component has previously been active
if (component.EnabledInHierarchy) component.Do(component.OnDisable);
if (component.HasStarted) component.Do(component.OnDestroy); // OnDestroy is only called if the component has previously been active
component.Dispose();
}
_components.Clear();
Expand Down Expand Up @@ -710,13 +734,4 @@ private void HandleMissingComponent(SerializedProperty compTag, Serializer.Seria
}
}
}

public void OnBeforeSerialize() { }

public void OnAfterDeserialize()
{
// Trigger awake after everything has been Deserialized
foreach (var comp in _components)
comp.TriggerAwake();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

namespace Prowl.Runtime;

// TODO: https://docs.unity3d.com/540/Documentation/Manual/ExecutionOrder.html
// Need to work on mimicng Unity's Execution Order

public abstract class MonoBehaviour : EngineObject
{
public static bool PauseLogic = false;
private static Dictionary<Type, bool> CachedExecuteAlways = new();

[SerializeField, HideInInspector]
internal protected bool _enabled = true;
[SerializeField, HideInInspector]
Expand All @@ -34,9 +29,9 @@ public enum RenderingOrder { None, Opaque, Lighting }
public GameObject GameObject => _go;
public Transform Transform => _go.Transform;

private bool executeAlways = false;
private bool hasAwoken = false;
public bool HasStarted { get; private set; } = false;
public bool ExecuteAlways { get; internal set; } = false;
public bool HasAwoken { get; internal set; } = false;
public bool HasStarted { get; internal set; } = false;

public string Tag => _go.tag;

Expand Down Expand Up @@ -87,24 +82,16 @@ internal void AttachToGameObject(GameObject go)
_enabledInHierarchy = isEnabled;
}

internal void TriggerAwake()
{
if(_enabledInHierarchy)
Try(AwakeAndEnable);
}

internal void HierarchyStateChanged()
{
bool newState = _enabled && _go.enabledInHierarchy;
if (newState != _enabledInHierarchy)
{
_enabledInHierarchy = newState;
if (newState)
{
Try(AwakeAndEnable);
}
Do(OnEnable);
else
Try(Internal_OnDisabled);
Do(OnDisable);
}
}

Expand Down Expand Up @@ -137,62 +124,38 @@ public virtual void DrawGizmosSelected() { }
public virtual void OnLevelWasLoaded() { }
public virtual void OnDestroy() { }

internal void AwakeAndEnable()
internal void InternalAwake()
{
if (!hasAwoken)
{
var t = GetType();
executeAlways = CachedExecuteAlways.TryGetValue(t, out bool value) ? value : (CachedExecuteAlways[t] = t.GetCustomAttribute<ExecuteAlwaysAttribute>() != null);
if (HasAwoken) return;
HasAwoken = true;
Awake();

if (!PauseLogic || executeAlways)
{
hasAwoken = true;
Awake();
}
}

// Always enable
if (!PauseLogic || executeAlways) OnEnable();
if (EnabledInHierarchy)
Do(OnEnable);
}
internal void Internal_OnDisabled()
internal void InternalStart()
{
if (!PauseLogic || executeAlways) OnDisable();
if (HasStarted) return;
HasStarted = true;
Start();
}
internal void Internal_Start()

private static Dictionary<Type, bool> CachedExecuteAlways = new();
internal void Do(Action action)
{
if (HasStarted) return;
if (!PauseLogic || executeAlways)
bool always;
if (CachedExecuteAlways.TryGetValue(GetType(), out bool value))
always = value;
else
{
HasStarted = true;
Start();
always = GetType().GetCustomAttribute<ExecuteAlwaysAttribute>() != null;
CachedExecuteAlways[GetType()] = always;
}
}
internal void Internal_FixedUpdate()
{
if (!PauseLogic || executeAlways) FixedUpdate();
}
internal void Internal_Update()
{
if (!PauseLogic || executeAlways) Update();
}
internal void Internal_LateUpdate()
{
if (!PauseLogic || executeAlways) LateUpdate();
}
internal void Internal_OnLevelWasLoaded()
{
if (!PauseLogic || executeAlways) OnLevelWasLoaded();
}
internal void Internal_OnDestroy()
{
if (!PauseLogic || executeAlways) OnDestroy();
}

internal static void Try(Action action)
{
try
{
action();
if (Application.isPlaying || always)
action();
}
catch (Exception e)
{
Expand Down
Loading

0 comments on commit 419f947

Please sign in to comment.