using Engine.Graphics.Camera; using Engine.Graphics.Pipeline; using Engine.Scene.Component.BuiltIn; namespace Engine.Scene; public class Scene : IUpdate, IRender { public bool IsPlaying { get; private set; } public IReadOnlyDictionary Cameras => _cameras; public float TimeScale { get; set; } = 1.0f; private readonly Dictionary _cameras = new(); internal Hierarchy Hierarchy { get; } = new(); private readonly Queue _sceneActions = []; internal void Enter() { if (IsPlaying) { throw new InvalidOperationException("Scene is already playing"); } ProcessChanges(); var allCameras = FindAllComponents(); foreach (var camera in allCameras) { _cameras.Add(camera.RenderLayer, camera); } IsPlaying = true; } public List FindAllComponents(bool parOnlyEnabled = true) where T : Component.Component { return Hierarchy.Objects .Where(parGameObject => !parOnlyEnabled || parGameObject.IsEnabled) .Select(parGameObject => parGameObject.GetComponent()) .Where(parComponent => parComponent != null).ToList()!; } public T? FindFirstComponent() where T : Component.Component { return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent()) .FirstOrDefault(parComponent => parComponent != null); } public void Update(double parDeltaTime) { if (!IsPlaying) { throw new InvalidOperationException("Scene is not playing"); } ProcessChanges(); var hierarchyObjects = Hierarchy.Objects; foreach (var gameObject in hierarchyObjects) { gameObject.PreUpdate(); } foreach (var gameObject in hierarchyObjects) { gameObject.Update(parDeltaTime * TimeScale); } } public void Render() { if (!IsPlaying) { throw new InvalidOperationException("Scene is not playing"); } foreach (var gameObject in Hierarchy.Objects) { gameObject.Render(); } } internal void Exit() { if (!IsPlaying) { throw new InvalidOperationException("Scene is not playing"); } foreach (var gameObject in Hierarchy.Objects) { gameObject.Destroy(); } IsPlaying = false; } public void Add(GameObject parGameObject) { parGameObject.Scene = this; Hierarchy.Add(parGameObject); } public void AddChild(GameObject parParent, GameObject parChild) { Add(parChild); Hierarchy.AddChild(parParent, parChild); } public void Remove(GameObject parGameObject) { var children = Hierarchy.GetAllChildren(parGameObject).ToList(); Hierarchy.Remove(parGameObject); _sceneActions.Enqueue(() => { foreach (var child in children) { child.Destroy(); child.Scene = null; } parGameObject.Destroy(); parGameObject.Scene = null; }); } public IEnumerable GetChildren(GameObject parParent, bool parRecursive = false) { return parRecursive ? Hierarchy.GetAllChildren(parParent) : Hierarchy.GetChildren(parParent); } private void ProcessChanges() { Hierarchy.ProcessChanges(); foreach (var gameObject in Hierarchy.Objects) { gameObject.ProcessChanges(); } while (_sceneActions.TryDequeue(out var action)) { action(); } } }