using System.ComponentModel; using System.Runtime.CompilerServices; namespace Sandbox; /// /// Allows creation of a system that always exists in every scene, is hooked into the scene's lifecycle, /// and is disposed when the scene is disposed. /// [Expose] public abstract partial class GameObjectSystem : IDisposable { public Scene Scene { get; private set; } List disposables = new List(); public GameObjectSystem( Scene scene ) { Scene = scene; Id = Guid.NewGuid(); } public virtual void Dispose() { foreach ( var d in disposables ) { d.Dispose(); } Scene = null; } /// /// Listen to a frame stage. Order is used to determine the order in which listeners are called, the default action always happens at 0, so if you /// want it to happen before you should go to -1, if you want it to happen after go to 1 etc. /// protected void Listen( Stage stage, int order, Action function, string debugName ) { var d = Scene.AddHook( stage, order, function, GetType().Name, debugName ); disposables.Add( d ); } /// /// A list of stages in the scene tick in which we can hook /// public enum Stage { /// /// At the very start of the scene update /// StartUpdate, /// /// Bones are worked out /// UpdateBones, /// /// Physics step, called in fixed update /// PhysicsStep, /// /// When transforms are interpolated /// Interpolation, /// /// At the very end of the scene update /// FinishUpdate, /// /// Called at the start of fixed update /// StartFixedUpdate, /// /// Called at the end of fixed update /// FinishFixedUpdate, /// /// Called after a scene has been loaded /// SceneLoaded, } /// /// When implementing an ITraceProvider, the most important thing to keep in mind /// is that the call to DoTrace should be thread safe. This might be called from /// multiple threads at once, so you better watch out. /// public interface ITraceProvider { public void DoTrace( in SceneTrace trace, List results ); public SceneTraceResult? DoTrace( in SceneTrace trace ); } } /// /// A syntax sugar wrapper around GameObjectSystem, which allows you to access your system using /// SystemName.Current instead of Scene.GetSystem. /// public abstract class GameObjectSystem : GameObjectSystem where T : GameObjectSystem { protected GameObjectSystem( Scene scene ) : base( scene ) { } public static T Current => Get( Game.ActiveScene ); public static T Get( Scene scene ) => scene?.GetSystem() ?? default; }