using GFramework.Core.system; using GFramework.Game.assets; using Godot; namespace GFramework.Godot.system; /// /// 资源加载系统,用于统一管理和缓存Godot资源(如场景、纹理等)的加载与实例化。 /// 提供基础加载、场景实例化、资源工厂注册以及缓存管理功能。 /// public class ResourceLoadSystem : AbstractSystem, IResourceLoadSystem { /// /// 已加载的资源缓存字典,键为资源路径,值为已加载的Resource对象。 /// private readonly Dictionary _loadedResources = new(); /// /// 场景懒加载器缓存,键为场景路径,值为延迟加载的PackedScene对象。 /// private readonly Dictionary> _sceneLoaders = new(); /// /// 场景实例化工厂委托缓存,键为场景路径,值为创建该场景实例的Func委托。 /// private readonly Dictionary _sceneFactories = new(); /// /// 资源获取/复制工厂委托缓存,键为资源路径,值为获取或复制资源的Func委托。 /// private readonly Dictionary _resourceFactories = new(); /// /// 初始化方法,在系统初始化时打印日志信息。 /// 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 /// /// 根据给定路径加载场景,并创建其节点实例。 /// /// 期望返回的节点类型,必须是Node的子类。 /// 场景文件的相对路径。 /// 新创建的场景根节点实例;如果加载失败则返回null。 public T? CreateInstance(string path) where T : Node { var scene = GetSceneLoader(path).Value; return scene.Instantiate(); } public Func GetOrRegisterGameUnitFactory(AssetCatalog.GameUnitId 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.TemplateId 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; } #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 }