Make most subsystems available as protected properties in base Game class, implement IDisposable on systems.

This commit is contained in:
2024-08-22 21:47:57 +02:00
parent c61a12d170
commit 03e7a4a90c
11 changed files with 136 additions and 65 deletions

View File

@@ -1,5 +1,3 @@
using System.Numerics;
using Voile; using Voile;
using Voile.Rendering; using Voile.Rendering;
using Voile.Audio; using Voile.Audio;
@@ -7,43 +5,16 @@ using Voile.Resources;
using Voile.SceneGraph; using Voile.SceneGraph;
using Voile.Utils; using Voile.Utils;
public class TestGame : Game public class TestGame : Game
{ {
public override string ResourceRoot => "Resources/"; public override string ResourceRoot => "Resources/";
public TestGame() : base()
{
}
public override void Initialize() public override void Initialize()
{ {
_renderer = new RaylibRenderer(); InitializeDefault();
_audioBackend = new FmodAudioBackend(); _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(); _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() protected override void LoadResources()
@@ -66,9 +37,20 @@ public class TestGame : Game
protected override void Ready() protected override void Ready()
{ {
_inputHandler!.AddInputMapping("play", new InputAction[] { new KeyInputAction(KeyboardKey.Spacebar) }); _scene = new Scene(new SceneSettings()
_inputHandler.AddInputMapping("sprint", new InputAction[] { new KeyInputAction(KeyboardKey.LeftShift) }); {
_inputHandler.AddInputMapping("toggle_fullscreen", new InputAction[] { new KeyInputAction(KeyboardKey.F11) }); 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!); _scene!.AddLayer("World", _worldLayer!);
@@ -91,15 +73,13 @@ public class TestGame : Game
public override void Shutdown() public override void Shutdown()
{ {
_renderer!.Shutdown(); ShutdownDefault();
_audioBackend?.Shutdown(); _audioBackend!.Dispose();
} }
private Renderer? _renderer;
private Sound? _testSound; private Sound? _testSound;
private Font? _font; private Font? _font;
private FmodAudioBackend? _audioBackend; private FmodAudioBackend? _audioBackend;
private InputHandler? _inputHandler;
private Scene? _scene; private Scene? _scene;
private UiLayer? _uiLayer; private UiLayer? _uiLayer;

View File

@@ -4,7 +4,7 @@ namespace Voile.Audio
{ {
public abstract void Initialize(); public abstract void Initialize();
public abstract void Update(); public abstract void Update();
public abstract void Shutdown(); protected abstract void Shutdown();
// BUS // BUS
public abstract void CreateBus(string busName); public abstract void CreateBus(string busName);
public abstract void SetBusVolume(string busName, float volume); public abstract void SetBusVolume(string busName, float volume);
@@ -26,6 +26,7 @@ namespace Voile.Audio
public void Dispose() public void Dispose()
{ {
Shutdown(); Shutdown();
GC.SuppressFinalize(this);
} }
private LehmerRandom _random = new LehmerRandom(); private LehmerRandom _random = new LehmerRandom();

View File

@@ -27,7 +27,7 @@ namespace Voile.Audio
return; return;
} }
public override void Shutdown() protected override void Shutdown()
{ {
return; return;
} }

View File

@@ -71,6 +71,11 @@ namespace Voile.Audio
channelGroup.addDSP(0, dsp); channelGroup.addDSP(0, dsp);
} }
protected override void Shutdown()
{
}
private DSP CreateReverbDsp(AudioEffectReverb effectReverb) private DSP CreateReverbDsp(AudioEffectReverb effectReverb)
{ {
DSP dsp; DSP dsp;
@@ -122,11 +127,6 @@ namespace Voile.Audio
return _channelGroups[busName]; return _channelGroups[busName];
} }
public override void Shutdown()
{
}
private FMOD.System _system; private FMOD.System _system;
// TODO: use a different key for the dictionary, paths are not good :( (waste of memory lol) // TODO: use a different key for the dictionary, paths are not good :( (waste of memory lol)

View File

@@ -1,3 +1,4 @@
using Voile.Rendering;
using Voile.Resources; using Voile.Resources;
namespace Voile namespace Voile
@@ -8,12 +9,36 @@ namespace Voile
/// The ResourceManager associated with this game. /// The ResourceManager associated with this game.
/// </summary> /// </summary>
protected ResourceManager ResourceManager { get; private set; } protected ResourceManager ResourceManager { get; private set; }
public abstract string ResourceRoot { get; } /// <summary>
/// The InputHandler associated with this game. Uses <see cref="RaylibInputHandler"/> by default.
/// </summary>
protected InputHandler Input { get; private set; }
/// <summary>
/// The Renderer associated with this game. Uses <see cref="RaylibRenderer"/> by default.
/// </summary>
protected Renderer Renderer { get; private set; }
/// <summary>
/// Resource root path for this game.
/// </summary>
public virtual string ResourceRoot => "Resources/";
/// <summary>
/// Path to the engine configuration file.
/// </summary>
public virtual string EngineConfigPath => "engine.config"; public virtual string EngineConfigPath => "engine.config";
/// <summary>
/// Name of this game. Also used as a default window title.
/// </summary>
public virtual string Name => "";
public Game() public Game()
{ {
ResourceManager = new ResourceManager(); ResourceManager = new ResourceManager();
Input = new RaylibInputHandler();
Renderer = new RaylibRenderer();
} }
/// <summary> /// <summary>
@@ -22,8 +47,6 @@ namespace Voile
/// </summary> /// </summary>
public void Start() public void Start()
{ {
Configure();
Initialize(); Initialize();
LoadResources(); LoadResources();
Ready(); Ready();
@@ -32,15 +55,36 @@ namespace Voile
} }
/// <summary> /// <summary>
/// Called when it's time to initialize the subsystems. /// Initializes subsystems using default types and settings.
/// </summary>
public void InitializeDefault()
{
ResourceManager.ResourceRoot = ResourceRoot;
Input = new RaylibInputHandler();
Renderer = new RaylibRenderer();
InitializeRenderer();
}
public void ShutdownDefault()
{
Input.Dispose();
Renderer.Dispose();
ResourceManager.Dispose();
}
/// <summary>
/// Called when it's time to initialize the subsystems, or modify default game settings or systems.
/// </summary> /// </summary>
public abstract void Initialize(); public abstract void Initialize();
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
protected abstract void LoadResources(); protected abstract void LoadResources();
/// <summary> /// <summary>
/// Called when it's safe to manipulate with the resources or/and systems. /// Called when it's safe to manipulate with the resources or/and systems.
/// You can safely create game objects, scenes, etc. in this method.
/// </summary> /// </summary>
protected abstract void Ready(); protected abstract void Ready();
/// <summary> /// <summary>
@@ -52,9 +96,14 @@ namespace Voile
/// </summary> /// </summary>
public abstract void Shutdown(); 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);
} }
} }
} }

View File

@@ -4,14 +4,18 @@ using Voile.Utils;
namespace Voile namespace Voile
{ {
public abstract class InputHandler public abstract class InputHandler : IDisposable
{ {
public static IReadOnlyDictionary<string, List<InputAction>> InputMappings => inputMappings;
public InputHandler() public InputHandler()
{ {
inputMappings = new Dictionary<string, IEnumerable<InputAction>>(); inputMappings = new Dictionary<string, List<InputAction>>();
CreateDefaultMappings(); CreateDefaultMappings();
} }
public Action OnInput;
public void Dispose() => GC.SuppressFinalize(this);
public bool Handled { get => _handled; set => _handled = value; } public bool Handled { get => _handled; set => _handled = value; }
public abstract bool IsKeyboardKeyDown(KeyboardKey key); public abstract bool IsKeyboardKeyDown(KeyboardKey key);
public abstract bool KeyboardKeyJustPressed(KeyboardKey key); public abstract bool KeyboardKeyJustPressed(KeyboardKey key);
@@ -36,7 +40,7 @@ namespace Voile
public void AddInputMapping(string actionName, IEnumerable<InputAction> inputActions) public void AddInputMapping(string actionName, IEnumerable<InputAction> inputActions)
{ {
inputMappings.Add(actionName, inputActions); inputMappings.Add(actionName, inputActions.ToList());
} }
private void CreateDefaultMappings() private void CreateDefaultMappings()
@@ -75,9 +79,9 @@ namespace Voile
return true; return true;
} }
private bool _handled;
protected Dictionary<string, IEnumerable<InputAction>> inputMappings;
protected static Dictionary<string, List<InputAction>> inputMappings = new();
private bool _handled;
private Logger _logger = new(nameof(InputHandler)); private Logger _logger = new(nameof(InputHandler));
} }

View File

@@ -100,7 +100,6 @@ namespace Voile
public override bool IsKeyboardKeyDown(KeyboardKey key) public override bool IsKeyboardKeyDown(KeyboardKey key)
{ {
Raylib_cs.KeyboardKey rayKey = (Raylib_cs.KeyboardKey)key; Raylib_cs.KeyboardKey rayKey = (Raylib_cs.KeyboardKey)key;
OnInput?.Invoke();
return Raylib.IsKeyDown(rayKey); return Raylib.IsKeyDown(rayKey);
} }

View File

@@ -82,7 +82,7 @@ namespace Voile.Rendering
return Raylib.WindowShouldClose(); return Raylib.WindowShouldClose();
} }
public override void Shutdown() protected override void Shutdown()
{ {
Raylib.CloseWindow(); Raylib.CloseWindow();
} }

View File

@@ -5,7 +5,7 @@ namespace Voile.Rendering
/// <summary> /// <summary>
/// An abstract class representing the graphics renderer. /// An abstract class representing the graphics renderer.
/// </summary> /// </summary>
public abstract class Renderer public abstract class Renderer : IDisposable
{ {
// INIT // INIT
/// <summary> /// <summary>
@@ -46,6 +46,13 @@ namespace Voile.Rendering
/// </summary> /// </summary>
public abstract Vector2 MonitorSize { get; } public abstract Vector2 MonitorSize { get; }
/// <inheritdoc/>
public void Dispose()
{
Shutdown();
GC.SuppressFinalize(this);
}
/// <summary> /// <summary>
/// Creates a window. /// Creates a window.
/// </summary> /// </summary>
@@ -59,7 +66,7 @@ namespace Voile.Rendering
protected abstract int GetMonitorHeight(int monitorId); protected abstract int GetMonitorHeight(int monitorId);
protected abstract int GetCurrentMonitor(); protected abstract int GetCurrentMonitor();
protected abstract bool WindowShouldClose(); protected abstract bool WindowShouldClose();
public abstract void Shutdown(); protected abstract void Shutdown();
// DRAWING // DRAWING
/// <summary> /// <summary>
@@ -171,7 +178,7 @@ namespace Voile.Rendering
public struct WindowSettings public struct WindowSettings
{ {
public string Title; public string Title;
public Vector2 Size = new Vector2(640, 480); public Vector2 Size = new Vector2(1280, 720);
public bool Resizable { get; set; } public bool Resizable { get; set; }
public WindowSettings(string title, Vector2 size) public WindowSettings(string title, Vector2 size)
@@ -179,5 +186,7 @@ namespace Voile.Rendering
Title = title; Title = title;
Size = size; Size = size;
} }
public static WindowSettings Default => new("Voile Game", new Vector2(1280, 720));
} }
} }

View File

@@ -130,7 +130,7 @@ namespace Voile.Rendering
/// <inheritdoc /> /// <inheritdoc />
public override void Shutdown() protected override void Shutdown()
{ {
_window!.DoEvents(); _window!.DoEvents();
_window.Reset(); _window.Reset();

View File

@@ -4,7 +4,7 @@ using Voile.Utils;
namespace Voile.Resources namespace Voile.Resources
{ {
public class ResourceManager public class ResourceManager : IDisposable
{ {
public string ResourceRoot { get; set; } = "Resources/"; public string ResourceRoot { get; set; } = "Resources/";
@@ -49,6 +49,24 @@ namespace Voile.Resources
return true; 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<T>(string path, in T resource) where T : Resource public bool TrySave<T>(string path, in T resource) where T : Resource
{ {
if (!TryGetSaver(out IResourceSaver<T>? saver)) if (!TryGetSaver(out IResourceSaver<T>? saver))
@@ -151,6 +169,17 @@ namespace Voile.Resources
return true; return true;
} }
public void Dispose()
{
foreach (var resource in _loadedResources)
{
TryUnload(resource.Key);
}
GC.SuppressFinalize(this);
}
private Logger _logger = new(nameof(ResourceManager)); private Logger _logger = new(nameof(ResourceManager));
private readonly Dictionary<Type, object> _resourceLoaderAssociations = new() private readonly Dictionary<Type, object> _resourceLoaderAssociations = new()