From 03e7a4a90cdca01b84755b595e07927976adf267 Mon Sep 17 00:00:00 2001 From: dnesov Date: Thu, 22 Aug 2024 21:47:57 +0200 Subject: [PATCH] Make most subsystems available as protected properties in base Game class, implement IDisposable on systems. --- TestGame/TestGame.cs | 56 +++++++------------ Voile/Source/Audio/AudioBackend.cs | 3 +- Voile/Source/Audio/DummyAudioBackend.cs | 2 +- Voile/Source/Audio/FmodAudioBackend.cs | 10 ++-- Voile/Source/Game.cs | 63 +++++++++++++++++++--- Voile/Source/InputHandler.cs | 16 +++--- Voile/Source/RaylibInputHandler.cs | 1 - Voile/Source/Rendering/RaylibRenderer.cs | 2 +- Voile/Source/Rendering/Renderer.cs | 15 ++++-- Voile/Source/Rendering/StandardRenderer.cs | 2 +- Voile/Source/Resources/ResourceManager.cs | 31 ++++++++++- 11 files changed, 136 insertions(+), 65 deletions(-) diff --git a/TestGame/TestGame.cs b/TestGame/TestGame.cs index 2d09539..c9653b9 100644 --- a/TestGame/TestGame.cs +++ b/TestGame/TestGame.cs @@ -1,5 +1,3 @@ -using System.Numerics; - using Voile; using Voile.Rendering; using Voile.Audio; @@ -7,43 +5,16 @@ using Voile.Resources; using Voile.SceneGraph; using Voile.Utils; - public class TestGame : Game { public override string ResourceRoot => "Resources/"; - public TestGame() : base() - { - } - public override void Initialize() { - _renderer = new RaylibRenderer(); + InitializeDefault(); + _audioBackend = new FmodAudioBackend(); - _inputHandler = new RaylibInputHandler(); - - _renderer.CreateAndInitialize(new WindowSettings() - { - Title = "Test Game", - Size = new Vector2(1280, 720), - }, new RendererSettings() - { - UseVSync = true, - Fullscreen = false - }); - _audioBackend.Initialize(); - - _scene = new Scene(new SceneSettings() - { - Renderer = _renderer, - AudioBackend = _audioBackend, - InputHandler = _inputHandler, - ResourceManager = ResourceManager - }); - - _uiLayer = new UiLayer(); - _worldLayer = new EntityLayer(); } protected override void LoadResources() @@ -66,9 +37,20 @@ public class TestGame : Game protected override void Ready() { - _inputHandler!.AddInputMapping("play", new InputAction[] { new KeyInputAction(KeyboardKey.Spacebar) }); - _inputHandler.AddInputMapping("sprint", new InputAction[] { new KeyInputAction(KeyboardKey.LeftShift) }); - _inputHandler.AddInputMapping("toggle_fullscreen", new InputAction[] { new KeyInputAction(KeyboardKey.F11) }); + _scene = new Scene(new SceneSettings() + { + Renderer = Renderer, + AudioBackend = _audioBackend!, + InputHandler = Input, + ResourceManager = ResourceManager + }); + + _uiLayer = new UiLayer(); + _worldLayer = new EntityLayer(); + + Input.AddInputMapping("play", new InputAction[] { new KeyInputAction(KeyboardKey.Spacebar) }); + Input.AddInputMapping("sprint", new InputAction[] { new KeyInputAction(KeyboardKey.LeftShift) }); + Input.AddInputMapping("toggle_fullscreen", new InputAction[] { new KeyInputAction(KeyboardKey.F11) }); _scene!.AddLayer("World", _worldLayer!); @@ -91,15 +73,13 @@ public class TestGame : Game public override void Shutdown() { - _renderer!.Shutdown(); - _audioBackend?.Shutdown(); + ShutdownDefault(); + _audioBackend!.Dispose(); } - private Renderer? _renderer; private Sound? _testSound; private Font? _font; private FmodAudioBackend? _audioBackend; - private InputHandler? _inputHandler; private Scene? _scene; private UiLayer? _uiLayer; diff --git a/Voile/Source/Audio/AudioBackend.cs b/Voile/Source/Audio/AudioBackend.cs index b956705..410e8a9 100644 --- a/Voile/Source/Audio/AudioBackend.cs +++ b/Voile/Source/Audio/AudioBackend.cs @@ -4,7 +4,7 @@ namespace Voile.Audio { public abstract void Initialize(); public abstract void Update(); - public abstract void Shutdown(); + protected abstract void Shutdown(); // BUS public abstract void CreateBus(string busName); public abstract void SetBusVolume(string busName, float volume); @@ -26,6 +26,7 @@ namespace Voile.Audio public void Dispose() { Shutdown(); + GC.SuppressFinalize(this); } private LehmerRandom _random = new LehmerRandom(); diff --git a/Voile/Source/Audio/DummyAudioBackend.cs b/Voile/Source/Audio/DummyAudioBackend.cs index 79a37c3..3a4b172 100644 --- a/Voile/Source/Audio/DummyAudioBackend.cs +++ b/Voile/Source/Audio/DummyAudioBackend.cs @@ -27,7 +27,7 @@ namespace Voile.Audio return; } - public override void Shutdown() + protected override void Shutdown() { return; } diff --git a/Voile/Source/Audio/FmodAudioBackend.cs b/Voile/Source/Audio/FmodAudioBackend.cs index 3674042..58bd888 100644 --- a/Voile/Source/Audio/FmodAudioBackend.cs +++ b/Voile/Source/Audio/FmodAudioBackend.cs @@ -71,6 +71,11 @@ namespace Voile.Audio channelGroup.addDSP(0, dsp); } + protected override void Shutdown() + { + + } + private DSP CreateReverbDsp(AudioEffectReverb effectReverb) { DSP dsp; @@ -122,11 +127,6 @@ namespace Voile.Audio return _channelGroups[busName]; } - public override void Shutdown() - { - - } - private FMOD.System _system; // TODO: use a different key for the dictionary, paths are not good :( (waste of memory lol) diff --git a/Voile/Source/Game.cs b/Voile/Source/Game.cs index 6d68552..432d361 100644 --- a/Voile/Source/Game.cs +++ b/Voile/Source/Game.cs @@ -1,3 +1,4 @@ +using Voile.Rendering; using Voile.Resources; namespace Voile @@ -8,12 +9,36 @@ namespace Voile /// The ResourceManager associated with this game. /// protected ResourceManager ResourceManager { get; private set; } - public abstract string ResourceRoot { get; } + /// + /// The InputHandler associated with this game. Uses by default. + /// + protected InputHandler Input { get; private set; } + + /// + /// The Renderer associated with this game. Uses by default. + /// + protected Renderer Renderer { get; private set; } + + /// + /// Resource root path for this game. + /// + public virtual string ResourceRoot => "Resources/"; + + /// + /// Path to the engine configuration file. + /// public virtual string EngineConfigPath => "engine.config"; + /// + /// Name of this game. Also used as a default window title. + /// + public virtual string Name => ""; + public Game() { ResourceManager = new ResourceManager(); + Input = new RaylibInputHandler(); + Renderer = new RaylibRenderer(); } /// @@ -22,8 +47,6 @@ namespace Voile /// public void Start() { - Configure(); - Initialize(); LoadResources(); Ready(); @@ -32,15 +55,36 @@ namespace Voile } /// - /// Called when it's time to initialize the subsystems. + /// Initializes subsystems using default types and settings. + /// + public void InitializeDefault() + { + ResourceManager.ResourceRoot = ResourceRoot; + + Input = new RaylibInputHandler(); + Renderer = new RaylibRenderer(); + + InitializeRenderer(); + } + + public void ShutdownDefault() + { + Input.Dispose(); + Renderer.Dispose(); + ResourceManager.Dispose(); + } + + /// + /// Called when it's time to initialize the subsystems, or modify default game settings or systems. /// public abstract void Initialize(); /// - /// Called when it's time to load the application's resources, such as images or sounds. + /// Called when it's time to load the game's resources, such as images or sounds. /// protected abstract void LoadResources(); /// /// Called when it's safe to manipulate with the resources or/and systems. + /// You can safely create game objects, scenes, etc. in this method. /// protected abstract void Ready(); /// @@ -52,9 +96,14 @@ namespace Voile /// public abstract void Shutdown(); - private void Configure() + private void InitializeRenderer() { - ResourceManager.ResourceRoot = ResourceRoot; + var windowSettings = WindowSettings.Default; + + if (!string.IsNullOrWhiteSpace(Name)) + windowSettings.Title = Name; + + Renderer.CreateAndInitialize(windowSettings, RendererSettings.Default); } } } \ No newline at end of file diff --git a/Voile/Source/InputHandler.cs b/Voile/Source/InputHandler.cs index d5cc2d9..17ff325 100644 --- a/Voile/Source/InputHandler.cs +++ b/Voile/Source/InputHandler.cs @@ -4,14 +4,18 @@ using Voile.Utils; namespace Voile { - public abstract class InputHandler + public abstract class InputHandler : IDisposable { + public static IReadOnlyDictionary> InputMappings => inputMappings; + public InputHandler() { - inputMappings = new Dictionary>(); + inputMappings = new Dictionary>(); CreateDefaultMappings(); } - public Action OnInput; + + public void Dispose() => GC.SuppressFinalize(this); + public bool Handled { get => _handled; set => _handled = value; } public abstract bool IsKeyboardKeyDown(KeyboardKey key); public abstract bool KeyboardKeyJustPressed(KeyboardKey key); @@ -36,7 +40,7 @@ namespace Voile public void AddInputMapping(string actionName, IEnumerable inputActions) { - inputMappings.Add(actionName, inputActions); + inputMappings.Add(actionName, inputActions.ToList()); } private void CreateDefaultMappings() @@ -75,9 +79,9 @@ namespace Voile return true; } - private bool _handled; - protected Dictionary> inputMappings; + protected static Dictionary> inputMappings = new(); + private bool _handled; private Logger _logger = new(nameof(InputHandler)); } diff --git a/Voile/Source/RaylibInputHandler.cs b/Voile/Source/RaylibInputHandler.cs index a731525..beabd0a 100644 --- a/Voile/Source/RaylibInputHandler.cs +++ b/Voile/Source/RaylibInputHandler.cs @@ -100,7 +100,6 @@ namespace Voile public override bool IsKeyboardKeyDown(KeyboardKey key) { Raylib_cs.KeyboardKey rayKey = (Raylib_cs.KeyboardKey)key; - OnInput?.Invoke(); return Raylib.IsKeyDown(rayKey); } diff --git a/Voile/Source/Rendering/RaylibRenderer.cs b/Voile/Source/Rendering/RaylibRenderer.cs index 1071df1..0eda918 100644 --- a/Voile/Source/Rendering/RaylibRenderer.cs +++ b/Voile/Source/Rendering/RaylibRenderer.cs @@ -82,7 +82,7 @@ namespace Voile.Rendering return Raylib.WindowShouldClose(); } - public override void Shutdown() + protected override void Shutdown() { Raylib.CloseWindow(); } diff --git a/Voile/Source/Rendering/Renderer.cs b/Voile/Source/Rendering/Renderer.cs index 85ceb4e..02ef916 100644 --- a/Voile/Source/Rendering/Renderer.cs +++ b/Voile/Source/Rendering/Renderer.cs @@ -5,7 +5,7 @@ namespace Voile.Rendering /// /// An abstract class representing the graphics renderer. /// - public abstract class Renderer + public abstract class Renderer : IDisposable { // INIT /// @@ -46,6 +46,13 @@ namespace Voile.Rendering /// public abstract Vector2 MonitorSize { get; } + /// + public void Dispose() + { + Shutdown(); + GC.SuppressFinalize(this); + } + /// /// Creates a window. /// @@ -59,7 +66,7 @@ namespace Voile.Rendering protected abstract int GetMonitorHeight(int monitorId); protected abstract int GetCurrentMonitor(); protected abstract bool WindowShouldClose(); - public abstract void Shutdown(); + protected abstract void Shutdown(); // DRAWING /// @@ -171,7 +178,7 @@ namespace Voile.Rendering public struct WindowSettings { public string Title; - public Vector2 Size = new Vector2(640, 480); + public Vector2 Size = new Vector2(1280, 720); public bool Resizable { get; set; } public WindowSettings(string title, Vector2 size) @@ -179,5 +186,7 @@ namespace Voile.Rendering Title = title; Size = size; } + + public static WindowSettings Default => new("Voile Game", new Vector2(1280, 720)); } } \ No newline at end of file diff --git a/Voile/Source/Rendering/StandardRenderer.cs b/Voile/Source/Rendering/StandardRenderer.cs index 3787ce1..3bdd14f 100644 --- a/Voile/Source/Rendering/StandardRenderer.cs +++ b/Voile/Source/Rendering/StandardRenderer.cs @@ -130,7 +130,7 @@ namespace Voile.Rendering /// - public override void Shutdown() + protected override void Shutdown() { _window!.DoEvents(); _window.Reset(); diff --git a/Voile/Source/Resources/ResourceManager.cs b/Voile/Source/Resources/ResourceManager.cs index cb16586..3b239a1 100644 --- a/Voile/Source/Resources/ResourceManager.cs +++ b/Voile/Source/Resources/ResourceManager.cs @@ -4,7 +4,7 @@ using Voile.Utils; namespace Voile.Resources { - public class ResourceManager + public class ResourceManager : IDisposable { public string ResourceRoot { get; set; } = "Resources/"; @@ -49,6 +49,24 @@ namespace Voile.Resources return true; } + public bool TryUnload(string resourceId) + { + _logger.Info($"Unloading resource with id \"{resourceId}\"..."); + + if (!_loadedResources.ContainsKey(resourceId)) + { + _logger.Error($"Cannot unload resource with id \"{resourceId}\": resource doesn't exist!"); + return false; + } + + var resource = _loadedResources[resourceId]; + + _loadedResources.Remove(resourceId); + resource.Dispose(); + + return true; + } + public bool TrySave(string path, in T resource) where T : Resource { if (!TryGetSaver(out IResourceSaver? saver)) @@ -151,6 +169,17 @@ namespace Voile.Resources return true; } + public void Dispose() + { + foreach (var resource in _loadedResources) + { + TryUnload(resource.Key); + } + + GC.SuppressFinalize(this); + } + + private Logger _logger = new(nameof(ResourceManager)); private readonly Dictionary _resourceLoaderAssociations = new()