refactor(asset): 移除抽象资源目录工具并重构注册表接口

- 删除 AbstractAssetCatalogUtility 类及其所有实现逻辑
- 移除 AssetCatalog 相关结构体和接口定义
- 删除 IAssetCatalogUtility 和 IResourceFactoryUtility 接口
- 移除 AbstractResourceFactoryUtility 和 ResourceLoadUtility 实现
- 重命名 IUiRegistry 接口为 IAssetRegistry 并更新泛型参数说明
- 重命名 GodotUiRegistry 为 GodotAssetRegistry 并更新接口引用
- 创建新的 IGodotAssetRegistry 接口用于PackedScene资源管理
- 创建新的 IGodotSceneRegistry 接口并更新 GodotSceneRegistry 实现
- 更新 GodotUiFactory 中的依赖注入从 IUiRegistry 改为 IAssetRegistry
This commit is contained in:
GeWuYou 2026-01-18 20:14:33 +08:00
parent 3589a0cf2f
commit 078f5c2102
15 changed files with 32 additions and 913 deletions

View File

@ -1,45 +0,0 @@
namespace GFramework.Game.Abstractions.assets;
/// <summary>
/// 资源目录类,用于定义和管理游戏中的场景和资源标识符
/// </summary>
public static class AssetCatalog
{
/// <summary>
/// 资源标识符接口,定义了资源路径的访问接口
/// </summary>
public interface IAssetId
{
/// <summary>
/// 获取资源的路径
/// </summary>
string Path { get; }
}
/// <summary>
/// 资源目录映射结构体,用于存储资源目录的键值对映射关系
/// </summary>
/// <param name="Key">资源目录的键</param>
/// <param name="Id">资源标识符</param>
public readonly record struct AssetCatalogMapping(string Key, IAssetId Id);
/// <summary>
/// 场景页面资源标识符结构体,用于标识场景页面资源
/// </summary>
/// <param name="Path">场景页面资源路径</param>
public readonly record struct ScenePageId(string Path) : IAssetId;
/// <summary>
/// 场景单元资源标识符结构体,用于标识场景单元资源
/// </summary>
/// <param name="Path">场景单元资源路径</param>
public readonly record struct SceneUnitId(string Path) : IAssetId;
/// <summary>
/// 通用资源标识符结构体实现IAssetId接口
/// </summary>
/// <param name="Path">资源路径</param>
public readonly record struct AssetId(string Path) : IAssetId;
}

View File

@ -1,94 +0,0 @@
using GFramework.Core.Abstractions.utility;
namespace GFramework.Game.Abstractions.assets;
/// <summary>
/// 资产目录工具接口,提供对场景单元、场景页面和普通资产的管理功能
/// 继承自IUtility接口用于处理资产目录相关的操作
/// </summary>
public interface IAssetCatalogUtility : IContextUtility
{
/// <summary>
/// 根据指定的键获取场景单元标识符
/// </summary>
/// <param name="key">用于查找场景单元的键值</param>
/// <returns>返回与指定键对应的场景单元标识符</returns>
AssetCatalog.SceneUnitId GetSceneUnit(string key);
/// <summary>
/// 根据指定的键获取场景页面标识符
/// </summary>
/// <param name="key">用于查找场景页面的键值</param>
/// <returns>返回与指定键对应的场景页面标识符</returns>
AssetCatalog.ScenePageId GetScenePage(string key);
/// <summary>
/// 根据指定的键获取资源ID
/// </summary>
/// <param name="key">用于查找资源的键值</param>
/// <returns>返回对应的资源ID如果未找到则返回默认值</returns>
AssetCatalog.AssetId GetAsset(string key);
/// <summary>
/// 注册场景单元到资产目录中
/// </summary>
/// <param name="key">场景单元的唯一标识键</param>
/// <param name="path">场景单元资源的路径</param>
public void RegisterSceneUnit(string key, string path);
/// <summary>
/// 通过资产目录映射注册场景单元
/// </summary>
/// <param name="mapping">包含场景单元信息的资产目录映射对象</param>
public void RegisterSceneUnit(AssetCatalog.AssetCatalogMapping mapping);
/// <summary>
/// 注册场景页面模板
/// </summary>
/// <param name="key">场景页面的唯一标识键</param>
/// <param name="path">场景页面资源路径</param>
void RegisterScenePage(string key, string path);
/// <summary>
/// 通过资产目录映射注册场景页面
/// </summary>
/// <param name="mapping">包含场景页面信息的资产目录映射对象</param>
void RegisterScenePage(AssetCatalog.AssetCatalogMapping mapping);
/// <summary>
/// 注册普通资产资源到资产目录中
/// </summary>
/// <param name="key">资产的唯一标识键值</param>
/// <param name="path">资产资源的路径</param>
void RegisterAsset(string key, string path);
/// <summary>
/// 根据映射配置注册普通资产资源到资产目录中
/// </summary>
/// <param name="mapping">包含键值和路径映射关系的配置对象</param>
void RegisterAsset(AssetCatalog.AssetCatalogMapping mapping);
/// <summary>
/// 检查是否存在指定键的场景单元
/// </summary>
/// <param name="key">用于查找场景单元的键值</param>
/// <returns>存在返回true否则返回false</returns>
bool HasSceneUnit(string key);
/// <summary>
/// 检查是否存在指定键的场景页面
/// </summary>
/// <param name="key">用于查找场景页面的键值</param>
/// <returns>存在返回true否则返回false</returns>
bool HasScenePage(string key);
/// <summary>
/// 检查是否存在指定键的资源
/// </summary>
/// <param name="key">用于查找资源的键值</param>
/// <returns>存在返回true否则返回false</returns>
bool HasAsset(string key);
}

View File

@ -1,27 +0,0 @@
using System;
using GFramework.Core.Abstractions.utility;
namespace GFramework.Game.Abstractions.assets;
/// <summary>
/// 资源工厂工具接口,提供根据键名或资产目录映射获取资源创建函数的功能
/// 继承自IContextUtility接口用于在游戏框架中管理资源创建工厂
/// </summary>
public interface IResourceFactoryUtility : IContextUtility
{
/// <summary>
/// 根据指定键名获取指定类型T的资源创建函数
/// </summary>
/// <typeparam name="T">要获取创建函数的资源类型</typeparam>
/// <param name="key">用于标识资源的键名</param>
/// <returns>返回一个创建T类型实例的函数委托</returns>
Func<T> GetFactory<T>(string key);
/// <summary>
/// 根据资产目录映射获取指定类型T的资源创建函数
/// </summary>
/// <typeparam name="T">要获取创建函数的资源类型</typeparam>
/// <param name="mapping">资产目录映射信息</param>
/// <returns>返回一个创建T类型实例的函数委托</returns>
Func<T> GetFactory<T>(AssetCatalog.AssetCatalogMapping mapping);
}

View File

@ -1,132 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace GFramework.Game.Abstractions.assets;
/// <summary>
/// 资源工厂类,用于注册和解析各种资源的创建工厂
/// </summary>
public static class ResourceFactory
{
/// <summary>
/// 可预加载条目接口,定义了是否需要预加载以及执行工厂的方法
/// </summary>
private interface IPreloadableEntry
{
/// <summary>
/// 获取一个值,表示该资源是否需要预加载
/// </summary>
bool Preload { get; }
/// <summary>
/// 获取资源类型
/// </summary>
Type ResourceType { get; }
/// <summary>
/// 获取资源键值
/// </summary>
string Key { get; }
/// <summary>
/// 执行与该条目关联的工厂方法
/// </summary>
void ExecuteFactory();
}
/// <summary>
/// 表示一个具体的资源工厂条目,实现 IPreloadableEntry 接口
/// </summary>
/// <typeparam name="T">资源类型</typeparam>
private sealed class Entry<T>(string key, Func<T> factory, bool preload) : IPreloadableEntry
{
/// <summary>
/// 获取用于创建资源的工厂函数
/// </summary>
public Func<T> Factory { get; } = factory;
/// <summary>
/// 获取一个值,表示该资源是否需要预加载
/// </summary>
public bool Preload { get; } = preload;
/// <summary>
/// 执行工厂函数以创建资源实例
/// </summary>
public void ExecuteFactory()
{
Factory();
}
/// <summary>
/// 获取资源的类型
/// </summary>
public Type ResourceType => typeof(T);
/// <summary>
/// 获取资源的键值
/// </summary>
public string Key { get; } = key;
}
/// <summary>
/// 工厂注册表,管理所有已注册的资源工厂
/// </summary>
public sealed class Registry
{
/// <summary>
/// 存储所有已注册的工厂函数,键为资源类型,值为对应的工厂条目对象
/// </summary>
private readonly Dictionary<(Type type, string key), IPreloadableEntry> _factories = new();
/// <summary>
/// 注册指定类型的资源工厂
/// </summary>
/// <typeparam name="T">要注册的资源类型</typeparam>
/// <param name="key">键</param>
/// <param name="factory">创建该类型资源的工厂函数</param>
/// <param name="preload">是否需要预加载该资源默认为false</param>
public void Register<T>(string key, Func<T> factory, bool preload = false)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentException("Resource key cannot be null or empty.", nameof(key));
var dictKey = (typeof(T), key);
_factories[dictKey] = new Entry<T>(key, factory, preload);
}
/// <summary>
/// 解析并获取指定类型的工厂函数
/// </summary>
/// <typeparam name="T">要获取工厂函数的资源类型</typeparam>
/// <param name="key">资源键</param>
/// <returns>指定类型的工厂函数</returns>
/// <exception cref="InvalidOperationException">当指定类型的工厂未注册时抛出异常</exception>
public Func<T> ResolveFactory<T>(string key)
{
var dictKey = (typeof(T), key);
if (_factories.TryGetValue(dictKey, out var entry)
&& entry is Entry<T> typed)
return typed.Factory;
throw new InvalidOperationException(
$"Factory not registered: {typeof(T).Name} with key '{key}'");
}
/// <summary>
/// 预加载所有标记为需要预加载的资源
/// </summary>
public void PreloadAll()
{
// 遍历所有已注册的工厂
foreach (var entry in _factories.Values.Where(entry => entry.Preload))
// 执行其工厂方法进行预加载
entry.ExecuteFactory();
}
}
}

View File

@ -0,0 +1,10 @@
using GFramework.Core.Abstractions.utility;
using GFramework.Game.Abstractions.registry;
namespace GFramework.Game.Abstractions.ui;
/// <summary>
/// 资源注册表接口用于管理指定类型T的资源注册和查找
/// </summary>
/// <typeparam name="T">资源的类型</typeparam>
public interface IAssetRegistry<T> : IUtility, IRegistry<string, T>;

View File

@ -1,10 +0,0 @@
using GFramework.Core.Abstractions.utility;
using GFramework.Game.Abstractions.registry;
namespace GFramework.Game.Abstractions.ui;
/// <summary>
/// UI注册表接口用于根据UI键获取对应的UI实例
/// </summary>
/// <typeparam name="T">UI实例的类型参数使用协变修饰符out</typeparam>
public interface IUiRegistry<T> : IUtility, IRegistry<string, T>;

View File

@ -1,187 +0,0 @@
using GFramework.Core.utility;
using GFramework.Game.Abstractions.assets;
namespace GFramework.Game.assets;
/// <summary>
/// 资源目录系统抽象基类,用于管理和注册游戏中的场景和资源。
/// 提供了统一的接口来注册和查询不同类型的资产(如游戏单元、模板、普通资源)。
/// 子类需要实现 <see cref="RegisterAssets" /> 方法以完成具体资产的注册逻辑。
/// </summary>
public abstract class AbstractAssetCatalogUtility : AbstractContextUtility, IAssetCatalogUtility
{
private readonly Dictionary<string, AssetCatalog.AssetId> _assets = new();
private readonly Dictionary<string, AssetCatalog.ScenePageId> _scenePages = new();
private readonly Dictionary<string, AssetCatalog.SceneUnitId> _sceneUnits = new();
/// <summary>
/// 系统初始化时调用,用于触发资产注册流程。
/// 此方法会调用抽象方法 <see cref="RegisterAssets" />,由子类提供实际注册逻辑。
/// </summary>
protected override void OnInit()
{
RegisterAssets();
}
/// <summary>
/// 抽象方法,必须在子类中重写。用于定义具体的资产注册逻辑。
/// 在此方法中应通过调用各种 Register 方法将资产信息添加到系统中。
/// </summary>
protected abstract void RegisterAssets();
#region Register or Module 使
/// <summary>
/// 注册场景单元到资产目录中
/// </summary>
/// <param name="key">场景单元的唯一标识键</param>
/// <param name="path">场景单元资源的路径</param>
/// <exception cref="InvalidOperationException">当指定的键已存在时抛出异常</exception>
public void RegisterSceneUnit(string key, string path)
{
// 尝试添加场景单元,如果键已存在则抛出异常
if (!_sceneUnits.TryAdd(key, new AssetCatalog.SceneUnitId(path)))
throw new InvalidOperationException($"SceneUnit key duplicated: {key}");
}
/// <summary>
/// 通过资产目录映射注册场景单元
/// </summary>
/// <param name="mapping">包含场景单元信息的资产目录映射对象</param>
/// <exception cref="InvalidOperationException">当映射ID不是SceneUnitId类型或键已存在时抛出异常</exception>
public void RegisterSceneUnit(AssetCatalog.AssetCatalogMapping mapping)
{
// 验证映射ID是否为SceneUnitId类型
if (mapping.Id is not AssetCatalog.SceneUnitId sceneId)
throw new InvalidOperationException("Mapping ID is not a SceneUnitId");
// 尝试添加场景单元,如果键已存在则抛出异常
if (!_sceneUnits.TryAdd(mapping.Key, sceneId))
throw new InvalidOperationException($"Scene key duplicated: {mapping.Key}");
}
/// <summary>
/// 注册场景页面模板
/// </summary>
/// <param name="key">场景页面的唯一标识键</param>
/// <param name="path">场景页面资源路径</param>
/// <exception cref="InvalidOperationException">当键已存在时抛出异常</exception>
public void RegisterScenePage(string key, string path)
{
if (!_scenePages.TryAdd(key, new AssetCatalog.ScenePageId(path)))
throw new InvalidOperationException($"Template key duplicated: {key}");
}
/// <summary>
/// 通过资产目录映射注册场景页面
/// </summary>
/// <param name="mapping">包含场景页面信息的资产目录映射对象</param>
/// <exception cref="InvalidOperationException">当映射ID不是ScenePageId类型或键已存在时抛出异常</exception>
public void RegisterScenePage(AssetCatalog.AssetCatalogMapping mapping)
{
// 验证映射ID是否为ScenePageId类型
if (mapping.Id is not AssetCatalog.ScenePageId templateId)
throw new InvalidOperationException("Mapping ID is not a ScenePageId");
// 尝试添加场景页面,如果键已存在则抛出异常
if (!_scenePages.TryAdd(mapping.Key, templateId))
throw new InvalidOperationException($"Template key duplicated: {mapping.Key}");
}
/// <summary>
/// 注册一个通用资源Asset使用指定的键和路径。
/// </summary>
/// <param name="key">唯一标识该资源的字符串键。</param>
/// <param name="path">该资源对应的资源路径。</param>
/// <exception cref="InvalidOperationException">当键已存在时抛出异常。</exception>
public void RegisterAsset(string key, string path)
{
if (!_assets.TryAdd(key, new AssetCatalog.AssetId(path)))
throw new InvalidOperationException($"Asset key duplicated: {key}");
}
/// <summary>
/// 根据映射对象注册一个通用资源Asset
/// </summary>
/// <param name="mapping">包含键与ID映射关系的对象。</param>
/// <exception cref="InvalidOperationException">
/// 当映射ID不是 <see cref="AssetCatalog.AssetId" /> 类型或键重复时抛出异常。
/// </exception>
public void RegisterAsset(AssetCatalog.AssetCatalogMapping mapping)
{
if (mapping.Id is not AssetCatalog.AssetId assetId)
throw new InvalidOperationException("Mapping ID is not a AssetId");
if (!_assets.TryAdd(mapping.Key, assetId))
throw new InvalidOperationException($"Asset key duplicated: {mapping.Key}");
}
#endregion
#region Query
/// <summary>
/// 根据指定的键获取场景单元标识符
/// </summary>
/// <param name="key">用于查找场景单元的键值</param>
/// <returns>返回与指定键对应的场景单元标识符</returns>
public AssetCatalog.SceneUnitId GetSceneUnit(string key)
{
return _sceneUnits[key];
}
/// <summary>
/// 根据指定的键获取场景页面标识符
/// </summary>
/// <param name="key">用于查找场景页面的键值</param>
/// <returns>返回与指定键对应的场景页面标识符</returns>
public AssetCatalog.ScenePageId GetScenePage(string key)
{
return _scenePages[key];
}
/// <summary>
/// 获取指定键对应的通用资源ID。
/// </summary>
/// <param name="key">要查找的通用资源键。</param>
/// <returns>对应的通用资源ID。</returns>
/// <exception cref="KeyNotFoundException">如果未找到指定键则抛出异常。</exception>
public AssetCatalog.AssetId GetAsset(string key)
{
return _assets[key];
}
/// <summary>
/// 检查是否存在指定键的场景单元
/// </summary>
/// <param name="key">用于查找场景单元的键值</param>
/// <returns>存在返回true否则返回false</returns>
public bool HasSceneUnit(string key)
{
return _sceneUnits.ContainsKey(key);
}
/// <summary>
/// 检查是否存在指定键的场景页面
/// </summary>
/// <param name="key">用于查找场景页面的键值</param>
/// <returns>存在返回true否则返回false</returns>
public bool HasScenePage(string key)
{
return _scenePages.ContainsKey(key);
}
/// <summary>
/// 判断是否存在指定键的通用资源。
/// </summary>
/// <param name="key">要检查的通用资源键。</param>
/// <returns>若存在返回 true否则返回 false。</returns>
public bool HasAsset(string key)
{
return _assets.ContainsKey(key);
}
#endregion
}

View File

@ -1,125 +0,0 @@
using GFramework.Core.extensions;
using GFramework.Core.utility;
using GFramework.Game.Abstractions.assets;
using Godot;
namespace GFramework.Godot.assets;
/// <summary>
/// 资源工厂系统抽象基类,用于统一管理各类资源的创建与预加载逻辑。
/// 提供注册场景和资源的方法,并通过依赖的资源加载系统和资产目录系统完成实际资源的获取与构造。
/// </summary>
public abstract class AbstractResourceFactoryUtility : AbstractContextUtility, IResourceFactoryUtility
{
private IAssetCatalogUtility? _assetCatalogUtility;
private ResourceFactory.Registry? _registry;
private IResourceLoadUtility? _resourceLoadUtility;
/// <summary>
/// 根据指定的键获取资源工厂函数。
/// </summary>
/// <typeparam name="T">资源类型</typeparam>
/// <param name="key">资源键</param>
/// <returns>返回创建指定类型资源的工厂函数</returns>
public Func<T> GetFactory<T>(string key)
{
return _registry!.ResolveFactory<T>(key);
}
/// <summary>
/// 根据资产目录映射信息获取资源工厂函数。
/// </summary>
/// <typeparam name="T">资源类型</typeparam>
/// <param name="mapping">资产目录映射信息</param>
/// <returns>返回创建指定类型资源的工厂函数</returns>
public Func<T> GetFactory<T>(AssetCatalog.AssetCatalogMapping mapping)
{
return _registry!.ResolveFactory<T>(mapping.Key);
}
/// <summary>
/// 系统初始化方法,在系统启动时执行一次。
/// 初始化资源注册表,并获取依赖的资源加载系统和资产目录系统。
/// 最后执行所有已注册资源的预加载操作。
/// </summary>
protected override void OnInit()
{
_registry = new ResourceFactory.Registry();
_resourceLoadUtility = this.GetUtility<IResourceLoadUtility>();
_assetCatalogUtility = this.GetUtility<IAssetCatalogUtility>();
RegisterResources();
_registry!.PreloadAll();
}
/// <summary>
/// 注册系统所需的各种资源类型。由子类实现具体注册逻辑。
/// </summary>
protected abstract void RegisterResources();
#region Register Helpers
/// <summary>
/// 注册场景单元到资源管理系统中
/// </summary>
/// <typeparam name="T">场景单元类型必须继承自Node</typeparam>
/// <param name="sceneUnitKey">场景单元键值,用于标识特定的场景单元资源</param>
/// <param name="preload">是否预加载该资源默认为false</param>
public void RegisterSceneUnit<T>(
string sceneUnitKey,
bool preload = false)
where T : Node
{
var id = _assetCatalogUtility!.GetSceneUnit(sceneUnitKey);
_registry!.Register(
sceneUnitKey,
_resourceLoadUtility!.GetOrRegisterGameUnitFactory<T>(id),
preload
);
}
/// <summary>
/// 注册场景页面到资源管理系统中
/// </summary>
/// <typeparam name="T">场景页面类型必须继承自Node</typeparam>
/// <param name="scenePageKey">场景页面键值,用于标识特定的场景页面资源</param>
/// <param name="preload">是否预加载该资源默认为false</param>
public void RegisterScenePage<T>(
string scenePageKey,
bool preload = false)
where T : Node
{
var id = _assetCatalogUtility!.GetScenePage(scenePageKey);
_registry!.Register(
scenePageKey,
_resourceLoadUtility!.GetOrRegisterTemplateFactory<T>(id),
preload
);
}
/// <summary>
/// 注册通用资产资源到资源管理系统中
/// </summary>
/// <typeparam name="T">资产类型必须继承自Resource</typeparam>
/// <param name="assetKey">资产键值,用于标识特定的资产资源</param>
/// <param name="preload">是否预加载该资源默认为false</param>
public void RegisterAsset<T>(
string assetKey,
bool preload = false)
where T : Resource
{
var id = _assetCatalogUtility!.GetAsset(assetKey);
_registry!.Register(
assetKey,
_resourceLoadUtility!.GetOrRegisterAssetFactory<T>(id),
preload
);
}
#endregion
}

View File

@ -1,83 +0,0 @@
using GFramework.Core.Abstractions.utility;
using GFramework.Game.Abstractions.assets;
using Godot;
namespace GFramework.Godot.assets;
/// <summary>
/// 资源加载系统接口,提供资源和场景的加载、实例化、预加载等功能
/// </summary>
public interface IResourceLoadUtility : IContextUtility
{
/// <summary>
/// 加载指定路径的资源
/// </summary>
/// <typeparam name="T">资源类型必须继承自Resource</typeparam>
/// <param name="path">资源路径</param>
/// <returns>加载的资源实例</returns>
public T? LoadResource<T>(string path) where T : Resource;
/// <summary>
/// 获取场景加载器,用于延迟加载场景
/// </summary>
/// <param name="path">场景路径</param>
/// <returns>场景的延迟加载包装器</returns>
public Lazy<PackedScene> GetSceneLoader(string path);
/// <summary>
/// 创建指定路径场景的实例
/// </summary>
/// <typeparam name="T">节点类型必须继承自Node</typeparam>
/// <param name="path">场景路径</param>
/// <returns>场景实例化的节点对象</returns>
public T? CreateInstance<T>(string path) where T : Node;
/// <summary>
/// 获取或注册游戏单位工厂函数
/// </summary>
/// <typeparam name="T">节点类型必须继承自Node</typeparam>
/// <param name="id">场景资源标识符</param>
/// <returns>创建场景实例的工厂函数</returns>
Func<T> GetOrRegisterGameUnitFactory<T>(
AssetCatalog.SceneUnitId id
) where T : Node;
/// <summary>
/// 获取或注册模板资源工厂函数
/// </summary>
/// <typeparam name="T">节点类型必须继承自Node</typeparam>
/// <param name="id">模板资源标识符</param>
/// <returns>创建模板实例的工厂函数</returns>
Func<T> GetOrRegisterTemplateFactory<T>(
AssetCatalog.ScenePageId id
) where T : Node;
/// <summary>
/// 获取或注册通用资产工厂函数
/// </summary>
/// <typeparam name="T">资源类型必须继承自Resource</typeparam>
/// <param name="id">资产资源标识符</param>
/// <param name="duplicate">是否对原始资源进行复制操作默认为false</param>
/// <returns>创建资产实例的工厂函数</returns>
Func<T> GetOrRegisterAssetFactory<T>(
AssetCatalog.AssetId id,
bool duplicate = false
) where T : Resource;
/// <summary>
/// 预加载指定路径的多个资源
/// </summary>
/// <param name="paths">需要预加载的资源路径集合</param>
public void Preload(IEnumerable<string> paths);
/// <summary>
/// 卸载指定路径的资源
/// </summary>
/// <param name="path">需要卸载的资源路径</param>
public void Unload(string path);
/// <summary>
/// 清除所有已加载的资源
/// </summary>
public void ClearAll();
}

View File

@ -1,202 +0,0 @@
using GFramework.Core.utility;
using GFramework.Game.Abstractions.assets;
using Godot;
namespace GFramework.Godot.assets;
/// <summary>
/// 资源加载系统用于统一管理和缓存Godot资源如场景、纹理等的加载与实例化。
/// 提供基础加载、场景实例化、资源工厂注册以及缓存管理功能。
/// </summary>
public class ResourceLoadUtility : AbstractContextUtility, IResourceLoadUtility
{
/// <summary>
/// 已加载的资源缓存字典键为资源路径值为已加载的Resource对象。
/// </summary>
private readonly Dictionary<string, Resource> _loadedResources = new();
/// <summary>
/// 资源获取/复制工厂委托缓存键为资源路径值为获取或复制资源的Func委托。
/// </summary>
private readonly Dictionary<string, Delegate> _resourceFactories = new();
/// <summary>
/// 场景实例化工厂委托缓存键为场景路径值为创建该场景实例的Func委托。
/// </summary>
private readonly Dictionary<string, Delegate> _sceneFactories = new();
/// <summary>
/// 场景懒加载器缓存键为场景路径值为延迟加载的PackedScene对象。
/// </summary>
private readonly Dictionary<string, Lazy<PackedScene>> _sceneLoaders = new();
/// <summary>
/// 根据给定路径加载场景,并创建其节点实例。
/// </summary>
/// <typeparam name="T">期望返回的节点类型必须是Node的子类。</typeparam>
/// <param name="path">场景文件的相对路径。</param>
/// <returns>新创建的场景根节点实例如果加载失败则返回null。</returns>
public T? CreateInstance<T>(string path) where T : Node
{
var scene = GetSceneLoader(path).Value;
return scene.Instantiate<T>();
}
public Func<T> GetOrRegisterGameUnitFactory<T>(AssetCatalog.SceneUnitId id) where T : Node
{
var path = id.Path;
if (_sceneFactories.TryGetValue(path, out var d))
return d as Func<T> ??
throw new InvalidCastException($"Factory for path '{path}' is not of type Func<{typeof(T)}>");
var factory = () =>
{
var scene = GetSceneLoader(path).Value
?? throw new InvalidOperationException($"Scene not loaded: {path}");
return scene.Instantiate<T>()
?? throw new InvalidOperationException($"Instantiate failed: {path}");
};
_sceneFactories[path] = factory;
return factory;
}
public Func<T> GetOrRegisterTemplateFactory<T>(AssetCatalog.ScenePageId id) where T : Node
{
var path = id.Path;
if (_sceneFactories.TryGetValue(path, out var d))
return d as Func<T> ??
throw new InvalidCastException($"Factory for path '{path}' is not of type Func<{typeof(T)}>");
var factory = () =>
{
var scene = GetSceneLoader(path).Value
?? throw new InvalidOperationException($"Scene not loaded: {path}");
return scene.Instantiate<T>()
?? throw new InvalidOperationException($"Instantiate failed: {path}");
};
_sceneFactories[path] = factory;
return factory;
}
public Func<T> GetOrRegisterAssetFactory<T>(AssetCatalog.AssetId id, bool duplicate = false) where T : Resource
{
var path = id.Path;
if (_resourceFactories.TryGetValue(path, out var d))
return d as Func<T> ??
throw new InvalidCastException($"Factory for path '{path}' is not of type Func<{typeof(T)}>");
var factory = () =>
{
var res = LoadResource<T>(path)
?? throw new InvalidOperationException($"Load failed: {path}");
if (!duplicate) return res;
return res.Duplicate() as T ?? res;
};
_resourceFactories[path] = factory;
return factory;
}
/// <summary>
/// 初始化方法,在系统初始化时打印日志信息。
/// </summary>
protected override void OnInit()
{
}
#region
/// <summary>
/// 加载指定类型的资源并进行缓存。如果资源已经加载过则直接从缓存中返回。
/// </summary>
/// <typeparam name="T">要加载的资源类型必须继承自Resource。</typeparam>
/// <param name="path">资源在项目中的相对路径。</param>
/// <returns>成功加载的资源对象若路径无效或加载失败则返回null。</returns>
public T? LoadResource<T>(string path) where T : Resource
{
if (string.IsNullOrEmpty(path))
return null;
if (_loadedResources.TryGetValue(path, out var cached))
return cached as T;
var res = GD.Load<T>(path);
if (res == null)
{
GD.PrintErr($"[ResourceLoadUtility] Load failed: {path}");
return null;
}
_loadedResources[path] = res;
return res;
}
/// <summary>
/// 获取一个场景的懒加载器用于按需加载PackedScene资源。
/// 若对应路径尚未注册加载器则会自动创建一个新的Lazy实例。
/// </summary>
/// <param name="path">场景文件的相对路径。</param>
/// <returns>表示该场景懒加载逻辑的Lazy&lt;PackedScene&gt;对象。</returns>
public Lazy<PackedScene> GetSceneLoader(string path)
{
if (_sceneLoaders.TryGetValue(path, out var loader))
return loader;
loader = new Lazy<PackedScene>(() =>
{
var scene = LoadResource<PackedScene>(path);
return scene ?? throw new InvalidOperationException($"Failed to load scene: {path}");
});
_sceneLoaders[path] = loader;
return loader;
}
#endregion
#region
/// <summary>
/// 预加载一组资源和场景到内存中以提升后续访问速度。
/// </summary>
/// <param name="paths">待预加载的资源路径集合。</param>
public void Preload(IEnumerable<string> paths)
{
foreach (var path in paths)
{
GetSceneLoader(path);
LoadResource<Resource>(path);
}
}
/// <summary>
/// 清除指定路径的所有相关缓存数据,包括资源、场景加载器及各类工厂。
/// </summary>
/// <param name="path">要卸载的资源路径。</param>
public void Unload(string path)
{
_loadedResources.Remove(path);
_sceneLoaders.Remove(path);
_sceneFactories.Remove(path);
_resourceFactories.Remove(path);
}
/// <summary>
/// 清空所有当前系统的资源缓存、加载器和工厂列表。
/// </summary>
public void ClearAll()
{
_loadedResources.Clear();
_sceneLoaders.Clear();
_sceneFactories.Clear();
_resourceFactories.Clear();
}
#endregion
}

View File

@ -1,5 +1,4 @@
using GFramework.Core.Abstractions.registries;
using GFramework.Game.Abstractions.ui;
using Godot;
namespace GFramework.Godot.scene;
@ -9,5 +8,5 @@ namespace GFramework.Godot.scene;
/// 继承自KeyValueRegistryBase使用字符串作为键PackedScene作为值进行存储
/// 实现IUiRegistry接口提供UI场景的注册和管理功能
/// </summary>
public class GodotSceneRegistry() : KeyValueRegistryBase<string, PackedScene>(StringComparer.Ordinal),
IUiRegistry<PackedScene>;
public class GodotSceneRegistry()
: KeyValueRegistryBase<string, PackedScene>(StringComparer.Ordinal), IGodotSceneRegistry;

View File

@ -0,0 +1,6 @@
using GFramework.Game.Abstractions.ui;
using Godot;
namespace GFramework.Godot.scene;
public interface IGodotSceneRegistry : IAssetRegistry<PackedScene>;

View File

@ -1,5 +1,4 @@
using GFramework.Core.Abstractions.registries;
using GFramework.Game.Abstractions.ui;
using Godot;
namespace GFramework.Godot.ui;
@ -9,5 +8,5 @@ namespace GFramework.Godot.ui;
/// 继承自KeyValueRegistryBase使用字符串作为键PackedScene作为值进行存储
/// 实现IUiRegistry接口提供UI场景的注册和管理功能
/// </summary>
public class GodotUiRegistry() : KeyValueRegistryBase<string, PackedScene>(StringComparer.Ordinal),
IUiRegistry<PackedScene>;
public class GodotAssetRegistry()
: KeyValueRegistryBase<string, PackedScene>(StringComparer.Ordinal), IGodotAssetRegistry;

View File

@ -14,7 +14,7 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
/// <summary>
/// UI注册表用于存储和获取PackedScene类型的UI资源
/// </summary>
private IUiRegistry<PackedScene> _registry = null!;
private IAssetRegistry<PackedScene> _registry = null!;
/// <summary>
/// 根据指定的UI键创建UI页面实例
@ -44,6 +44,6 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
/// </summary>
protected override void OnInit()
{
_registry = this.GetUtility<IUiRegistry<PackedScene>>()!;
_registry = this.GetUtility<IAssetRegistry<PackedScene>>()!;
}
}

View File

@ -0,0 +1,10 @@
using GFramework.Game.Abstractions.ui;
using Godot;
namespace GFramework.Godot.ui;
/// <summary>
/// Godot UI注册表接口用于管理PackedScene类型的UI资源注册和管理
/// 继承自通用UI注册表接口专门针对Godot引擎的PackedScene资源类型
/// </summary>
public interface IGodotAssetRegistry : IAssetRegistry<PackedScene>;