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