Cleaned up asset loading system, and made progress on game engine structure.

This commit is contained in:
Harrison Deng 2020-05-18 14:07:25 -05:00
parent 8e2510903f
commit 635ed9fb7d
11 changed files with 90 additions and 33 deletions

1
.vscode/launch.json vendored
View File

@ -4,6 +4,7 @@
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 KiB

View File

@ -9,11 +9,11 @@ namespace RecrownedGTK.AssetsSystem {
public AssetLoader() {
typeLoaders = new Dictionary<Type, ILoader>();
}
public void AddLoaderResolver(Type type, ILoader loader) {
if (typeLoaders.ContainsKey(type)) {
throw new InvalidOperationException(String.Format("The type {0} already exists in this resolver.", type));
public void AddLoaderResolver(ILoader loader) {
if (typeLoaders.ContainsKey(loader.resultingType)) {
throw new InvalidOperationException(String.Format("The type {0} already exists in this resolver.", loader.resultingType));
}
typeLoaders.Add(type, loader);
typeLoaders.Add(loader.resultingType, loader);
}
public void RemoveLoaderResolver(Type type) {
if (!typeLoaders.ContainsKey(type)) {
@ -21,6 +21,11 @@ namespace RecrownedGTK.AssetsSystem {
}
typeLoaders.Remove(type);
}
public void RemoveLoaderResolver(ILoader loader) {
RemoveLoaderResolver(loader.resultingType);
}
public IInfo Load(string path, Type type) {
if (!typeLoaders.ContainsKey(type)) {
throw new InvalidOperationException(String.Format("The type {0} doesn't exist in this resolver.", type));

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
@ -11,16 +12,10 @@ namespace RecrownedGTK.AssetsSystem
/// </summary>
public class AssetManager
{
private AssetLoader assetLoader;
readonly Queue<ContentData> queue;
Dictionary<string, IDisposable> assets;
/// <summary>
/// Path modifiers to change the path in which the content manager looks to load a file. Used for better organizing things while not needing to type entire path.
/// </summary>
public readonly AssetLoader assetLoader;
readonly Queue<AssetOutline> queue;
ConcurrentDictionary<string, IDisposable> assets;
private readonly Dictionary<Type, IAssetPathResolver> contentPathModifier;
/// <summary>
/// Used when no path modifier is defined for that specific type.
/// </summary>
public IAssetPathResolver normalPathModifier = new NormalAssetPathResolver();
volatile float progress;
volatile bool running;
@ -36,13 +31,21 @@ namespace RecrownedGTK.AssetsSystem
public float Progress { get { return progress; } }
/// <summary>
/// Wraps the <see cref="ContentManager"/>.
/// Creates an asset manager with a default asset loader.
/// </summary>
/// <param name="contentManager">The manager to wrap.</param>
public AssetManager()
public AssetManager() : this(new AssetLoader())
{
assets = new Dictionary<string, IDisposable>();
queue = new Queue<ContentData>();
}
/// <summary>
/// A manager for the given asset loader.
/// The manager will asynchronously load assets and store them.
/// </summary>
/// <param name="assetLoader">The asset loader to use for loading.</param>
public AssetManager(AssetLoader assetLoader) {
this.assetLoader = assetLoader;
assets = new ConcurrentDictionary<string, IDisposable>();
queue = new Queue<AssetOutline>();
contentPathModifier = new Dictionary<Type, IAssetPathResolver>();
}
@ -80,8 +83,7 @@ namespace RecrownedGTK.AssetsSystem
}
path = handler.Modify(assetName);
}
assets.Add(assetName, assetLoader.Load(path, type).CreateUseable());
assets.TryAdd(assetName, assetLoader.Load(path, type).CreateUseable());
}
/// <summary>
@ -110,7 +112,7 @@ namespace RecrownedGTK.AssetsSystem
{
if (!assets.ContainsKey(assetName))
{
queue.Enqueue(new ContentData(assetName, typeof(T), usePathModifier));
queue.Enqueue(new AssetOutline(assetName, typeof(T), usePathModifier));
Debug.WriteLine("Queued asset: " + assetName);
Update();
}
@ -145,7 +147,7 @@ namespace RecrownedGTK.AssetsSystem
{
lock (queue)
{
ContentData content = queue.Dequeue();
AssetOutline content = queue.Dequeue();
Load(content.assetName, content.type, content.usePathModifier);
tasksCompleted++;
progress = (float)tasksCompleted / totalTasks;
@ -169,7 +171,8 @@ namespace RecrownedGTK.AssetsSystem
{
((IDisposable)assets[name]).Dispose();
}
assets.Remove(name);
IDisposable disposable = null;
assets.TryRemove(name, out disposable);
}
}
}
@ -201,13 +204,13 @@ namespace RecrownedGTK.AssetsSystem
Debug.WriteLine("Unloaded all assets.");
}
}
private struct ContentData
private struct AssetOutline
{
internal Type type;
internal string assetName;
internal bool usePathModifier;
public ContentData(string assetName, Type type, bool usePathModifier)
public AssetOutline(string assetName, Type type, bool usePathModifier)
{
this.type = type;
this.assetName = assetName;

View File

@ -1,9 +1,12 @@
using System;
namespace RecrownedGTK.AssetsSystem.Information {
/// <summary>
/// Describes a serializable datatype.
/// Meant for use as a part of the asset pipeline.
/// </summary>
public interface IInfo
{
Type type {get;}
IDisposable CreateUseable();
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace RecrownedGTK.AssetsSystem.Information {
/// <summary>
/// An interface that describes an information class.
/// An information class is class that can produce a useable asset object.
/// In the asset pipeline, this is the second step, after loading the asset data.
/// </summary>
public interface IInformation
{
}
}

View File

@ -5,7 +5,7 @@ namespace RecrownedGTK.AssetsSystem.Information {
public struct TextureInfo : IInfo {
public readonly byte[] textureData;
int width, height;
public Type type => typeof(TextureData);
public Type resultingType => typeof(TextureData);
public TextureInfo(int width, int height, byte[] textureData) {
this.width = width;
this.height = height;

View File

@ -3,6 +3,7 @@ using RecrownedGTK.AssetsSystem.Information;
namespace RecrownedGTK.AssetsSystem.Loaders {
public interface ILoader
{
Type resultingType {get;}
IInfo load(string path);
}
}

View File

@ -7,6 +7,8 @@ using RecrownedGTK.Graphics;
namespace RecrownedGTK.AssetsSystem.Loaders {
public class TextureLoader : ILoader
{
public Type resultingType => typeof(TextureData);
public IInfo load(string path)
{
int width;

View File

@ -7,12 +7,8 @@ using RecrownedGTK.AssetsSystem;
namespace RecrownedGTK.Game {
public sealed class GameEngine : GameWindow {
private IState state;
public readonly Preferences preferences;
public readonly AssetManager assets;
public GameEngine(IState initialState, Preferences preferences, AssetManager assets) : base() {
public GameEngine(IState initialState) : base() {
this.state = initialState;
this.assets = assets;
this.preferences = preferences;
}
protected override void OnUpdateFrame(FrameEventArgs e) {
@ -34,7 +30,7 @@ namespace RecrownedGTK.Game {
}
protected override void OnClosed(EventArgs e) {
if (state.CloseRequested()) {
if (!state.CloseRequested()) {
((CancelEventArgs) e).Cancel = true;
}
base.OnClosed(e);

View File

@ -3,13 +3,47 @@ using OpenTK.Input;
namespace RecrownedGTK.Game {
public interface IState
{
/// <summary>
/// Called when this state is going to be shown.
/// </summary>
/// <param name="gameManager">The instance of the game manager that is requesting this.</param>
/// <returns>True of it's ready to be displayed, false otherwise.</returns>
bool Shown(GameEngine gameManager);
/// <summary>
/// Called on updating the state.
/// </summary>
/// <param name="time">The time that has passed since the last update in seconds.</param>
void Update(double time);
/// <summary>
/// Called for rendering things.
/// </summary>
/// <param name="time">The time that has elapsed since last rendering in seconds.</param>
void Render(double time);
/// <summary>
/// Called when this state is no longer being displayed.
/// </summary>
void Hidden();
/// <summary>
/// Called when the game window size is updated.
/// </summary>
/// <param name="width">The new width</param>
/// <param name="height">The new height</param>
void WindowSizeUpdate(int width, int height);
/// <summary>
/// When the game is being requested to be closed.
/// </summary>
/// <returns>True if closing is acceptable, or false otherwise.</returns>
bool CloseRequested();
/// <summary>
/// Called every update in the case that there should be a state change.
/// </summary>
/// <returns>Another state if ready to change states, or null if not.</returns>
IState ChangeState();
}