feat(core): 重构资源工厂系统以支持键控资源注册与解析

- 引入基于键的资源工厂注册机制,替换原有的类型唯一注册方式
- 更新资源工厂接口,新增 GetFactory 方法支持键名参数
- 修改内部注册表结构,使用 (Type, string) 元组作为工厂字典键
- 在注册场景和资源时传入键名,确保资源可按名称区分
- 增强资源工厂条目信息,添加 ResourceType 和 Key 属性
- 完善工厂解析逻辑,通过类型和键双重条件匹配目标工厂
- 添加对空键值的校验,防止无效键导致运行时错误
- 优化预加载流程,仅执行明确标记为预加载的资源工厂
This commit is contained in:
GwWuYou 2025-12-18 21:19:14 +08:00
parent 8123683e6e
commit 24fa11cf1b
3 changed files with 76 additions and 25 deletions

View File

@ -35,19 +35,30 @@ public abstract class AbstractResourceFactorySystem : AbstractSystem, IResourceF
_registry.PreloadAll();
});
}
/// <summary>
/// 注册系统所需的各种资源类型。由子类实现具体注册逻辑。
/// </summary>
protected abstract void RegisterResources();
/// <summary>
/// 获取指定类型的资源工厂函数。
/// 根据指定的键获取资源工厂函数。
/// </summary>
/// <typeparam name="T">要获取工厂的资源类型</typeparam>
/// <typeparam name="T">资源类型</typeparam>
/// <param name="key">资源键</param>
/// <returns>返回创建指定类型资源的工厂函数</returns>
public Func<T> Get<T>() => _registry!.Resolve<T>();
public Func<T> GetFactory<T>(string key)=>_registry!.ResolveFactory<T>(key);
/// <summary>
/// 根据资产目录映射信息获取资源工厂函数。
/// </summary>
/// <typeparam name="T">资源类型</typeparam>
/// <param name="mapping">资产目录映射信息</param>
/// <returns>返回创建指定类型资源的工厂函数</returns>
public Func<T> GetFactory<T>(AssetCatalog.AssetCatalogMapping mapping) => _registry!.ResolveFactory<T>(mapping.Key);
#region Register Helpers
/// <summary>
@ -65,6 +76,7 @@ public abstract class AbstractResourceFactorySystem : AbstractSystem, IResourceF
var id = _assetCatalogSystem!.GetScene(sceneKey);
_registry!.Register(
sceneKey,
_resourceLoadSystem!.GetOrRegisterSceneFactory<T>(id),
preload
);
@ -87,6 +99,7 @@ public abstract class AbstractResourceFactorySystem : AbstractSystem, IResourceF
var id = _assetCatalogSystem!.GetResource(resourceKey);
_registry!.Register(
resourceKey,
_resourceLoadSystem!.GetOrRegisterResourceFactory<T>(id, duplicate),
preload
);

View File

@ -8,9 +8,19 @@ namespace GFramework.Core.Godot.system;
public interface IResourceFactorySystem : ISystem
{
/// <summary>
/// 获取指定类型T的资源创建函数
/// 根据指定键名获取指定类型T的资源创建函数
/// </summary>
/// <typeparam name="T">要获取创建函数的资源类型</typeparam>
/// <param name="key">用于标识资源的键名</param>
/// <returns>返回一个创建T类型实例的函数委托</returns>
Func<T> Get<T>();
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

@ -19,13 +19,24 @@ public static class ResourceFactory
/// 执行与该条目关联的工厂方法
/// </summary>
void ExecuteFactory();
/// <summary>
/// 获取资源类型
/// </summary>
Type ResourceType { get; }
/// <summary>
/// 获取资源键值
/// </summary>
string Key { get; }
}
/// <summary>
/// 表示一个具体的资源工厂条目,实现 IPreloadableEntry 接口
/// </summary>
/// <typeparam name="T">资源类型</typeparam>
private sealed class Entry<T>(Func<T> factory, bool preload) : IPreloadableEntry
private sealed class Entry<T>(string key, Func<T> factory, bool preload) : IPreloadableEntry
{
/// <summary>
/// 获取用于创建资源的工厂函数
@ -41,8 +52,19 @@ public static class ResourceFactory
/// 执行工厂函数以创建资源实例
/// </summary>
public void ExecuteFactory() => Factory();
/// <summary>
/// 获取资源的类型
/// </summary>
public Type ResourceType => typeof(T);
/// <summary>
/// 获取资源的键值
/// </summary>
public string Key { get; } = key;
}
/// <summary>
/// 工厂注册表,管理所有已注册的资源工厂
/// </summary>
@ -51,50 +73,56 @@ public static class ResourceFactory
/// <summary>
/// 存储所有已注册的工厂函数,键为资源类型,值为对应的工厂条目对象
/// </summary>
private readonly Dictionary<Type, object> _factories = new();
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>(Func<T> factory, bool preload = false)
public void Register<T>(string key, Func<T> factory, bool preload = false)
{
_factories[typeof(T)] = new Entry<T>(factory, preload);
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> Resolve<T>()
public Func<T> ResolveFactory<T>(string key)
{
// 尝试从字典中查找对应类型的工厂条目
if (_factories.TryGetValue(typeof(T), out var obj)
&& obj is Entry<T> entry)
return entry.Factory;
var dictKey = (typeof(T), key);
// 若未找到则抛出异常
throw new InvalidOperationException($"Factory not registered: {typeof(T).Name}");
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)
foreach (var entry in _factories.Values.Where(entry => entry.Preload))
{
// 检查当前条目是否支持预加载且被标记为需预加载
if (entry is IPreloadableEntry preloadable && preloadable.Preload)
{
// 执行其工厂方法进行预加载
preloadable.ExecuteFactory();
}
// 执行其工厂方法进行预加载
entry.ExecuteFactory();
}
}
}