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()