mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
- 将所有小写的命名空间导入更正为首字母大写格式 - 统一 GFramework 框架的命名空间引用规范 - 修复 core、ecs、godot 等模块的命名空间导入错误 - 标准化文档示例代码中的 using 语句格式 - 确保所有文档中的命名空间引用保持一致性 - 更新 global using 语句以匹配正确的命名空间格式
15 KiB
15 KiB
title, description
| title | description |
|---|---|
| Godot 资源仓储系统 | Godot 资源仓储系统提供了 Godot Resource 的集中管理和高效加载功能。 |
Godot 资源仓储系统
概述
Godot 资源仓储系统是 GFramework.Godot 中用于管理 Godot Resource 资源的核心组件。它提供了基于键值对的资源存储、批量加载、路径扫描等功能,让你可以高效地组织和访问游戏中的各类资源。
通过资源仓储系统,你可以将 Godot 的 .tres 和 .res 资源文件集中管理,支持按键快速查找、批量预加载、递归扫描目录等功能,简化资源管理流程。
主要特性:
- 基于键值对的资源管理
- 支持 Godot Resource 类型
- 路径扫描和批量加载
- 递归目录遍历
- 类型安全的资源访问
- 与 GFramework 架构集成
核心概念
资源仓储接口
IResourceRepository<TKey, TResource> 定义了资源仓储的基本操作:
public interface IResourceRepository<in TKey, TResource> : IRepository<TKey, TResource>
where TResource : Resource
{
void LoadFromPath(IEnumerable<string> paths);
void LoadFromPath(params string[] paths);
void LoadFromPathRecursive(IEnumerable<string> paths);
void LoadFromPathRecursive(params string[] paths);
}
资源仓储实现
GodotResourceRepository<TKey, TResource> 提供了完整的实现:
public class GodotResourceRepository<TKey, TResource>
: IResourceRepository<TKey, TResource>
where TResource : Resource, IHasKey<TKey>
where TKey : notnull
{
public void Add(TKey key, TResource value);
public TResource Get(TKey key);
public bool TryGet(TKey key, out TResource value);
public IReadOnlyCollection<TResource> GetAll();
public bool Contains(TKey key);
public void Remove(TKey key);
public void Clear();
}
资源键接口
资源必须实现 IHasKey<TKey> 接口:
public interface IHasKey<out TKey>
{
TKey Key { get; }
}
基本用法
定义资源类型
using Godot;
using GFramework.Core.Abstractions.bases;
// 定义资源数据类
[GlobalClass]
public partial class ItemData : Resource, IHasKey<string>
{
[Export]
public string Id { get; set; }
[Export]
public string Name { get; set; }
[Export]
public string Description { get; set; }
[Export]
public Texture2D Icon { get; set; }
[Export]
public int MaxStack { get; set; } = 99;
// 实现 IHasKey 接口
public string Key => Id;
}
创建资源仓储
using GFramework.Godot.Data;
public class ItemRepository : GodotResourceRepository<string, ItemData>
{
public ItemRepository()
{
// 从指定路径加载所有物品资源
LoadFromPath("res://data/items");
}
}
注册到架构
using GFramework.Godot.Architecture;
public class GameArchitecture : AbstractArchitecture
{
protected override void InstallModules()
{
// 注册物品仓储
var itemRepo = new ItemRepository();
RegisterUtility<IResourceRepository<string, ItemData>>(itemRepo);
}
}
使用资源仓储
using Godot;
using GFramework.Godot.Extensions;
public partial class InventoryController : Node
{
private IResourceRepository<string, ItemData> _itemRepo;
public override void _Ready()
{
// 获取资源仓储
_itemRepo = this.GetUtility<IResourceRepository<string, ItemData>>();
// 使用资源
ShowItemInfo("sword_001");
}
private void ShowItemInfo(string itemId)
{
// 获取物品数据
if (_itemRepo.TryGet(itemId, out var itemData))
{
GD.Print($"物品: {itemData.Name}");
GD.Print($"描述: {itemData.Description}");
GD.Print($"最大堆叠: {itemData.MaxStack}");
}
else
{
GD.Print($"物品 {itemId} 不存在");
}
}
}
高级用法
递归加载资源
public class AssetRepository : GodotResourceRepository<string, AssetData>
{
public AssetRepository()
{
// 递归加载所有子目录中的资源
LoadFromPathRecursive("res://assets");
}
}
多路径加载
public class ConfigRepository : GodotResourceRepository<string, ConfigData>
{
public ConfigRepository()
{
// 从多个路径加载资源
LoadFromPath(
"res://config/gameplay",
"res://config/ui",
"res://config/audio"
);
}
}
动态添加资源
public partial class ResourceManager : Node
{
private IResourceRepository<string, ItemData> _itemRepo;
public override void _Ready()
{
_itemRepo = this.GetUtility<IResourceRepository<string, ItemData>>();
}
public void AddCustomItem(ItemData item)
{
// 动态添加资源
_itemRepo.Add(item.Id, item);
GD.Print($"添加物品: {item.Name}");
}
public void RemoveItem(string itemId)
{
// 移除资源
if (_itemRepo.Contains(itemId))
{
_itemRepo.Remove(itemId);
GD.Print($"移除物品: {itemId}");
}
}
}
获取所有资源
public partial class ItemListUI : Control
{
private IResourceRepository<string, ItemData> _itemRepo;
public override void _Ready()
{
_itemRepo = this.GetUtility<IResourceRepository<string, ItemData>>();
DisplayAllItems();
}
private void DisplayAllItems()
{
// 获取所有物品
var allItems = _itemRepo.GetAll();
GD.Print($"共有 {allItems.Count} 个物品:");
foreach (var item in allItems)
{
GD.Print($"- {item.Name} ({item.Id})");
}
}
}
资源预加载
public partial class GameInitializer : Node
{
public override async void _Ready()
{
await PreloadAllResources();
GD.Print("所有资源预加载完成");
}
private async Task PreloadAllResources()
{
// 预加载物品资源
var itemRepo = new ItemRepository();
this.RegisterUtility<IResourceRepository<string, ItemData>>(itemRepo);
// 预加载技能资源
var skillRepo = new SkillRepository();
this.RegisterUtility<IResourceRepository<string, SkillData>>(skillRepo);
// 预加载敌人资源
var enemyRepo = new EnemyRepository();
this.RegisterUtility<IResourceRepository<string, EnemyData>>(enemyRepo);
await Task.CompletedTask;
}
}
资源缓存管理
public class CachedResourceRepository<TKey, TResource>
where TResource : Resource, IHasKey<TKey>
where TKey : notnull
{
private readonly GodotResourceRepository<TKey, TResource> _repository;
private readonly Dictionary<TKey, TResource> _cache = new();
public CachedResourceRepository(params string[] paths)
{
_repository = new GodotResourceRepository<TKey, TResource>();
_repository.LoadFromPath(paths);
}
public TResource Get(TKey key)
{
// 先从缓存获取
if (_cache.TryGetValue(key, out var cached))
{
return cached;
}
// 从仓储获取并缓存
var resource = _repository.Get(key);
_cache[key] = resource;
return resource;
}
public void ClearCache()
{
_cache.Clear();
GD.Print("资源缓存已清空");
}
}
资源版本管理
[GlobalClass]
public partial class VersionedItemData : Resource, IHasKey<string>
{
[Export]
public string Id { get; set; }
[Export]
public string Name { get; set; }
[Export]
public int Version { get; set; } = 1;
public string Key => Id;
}
public class VersionedItemRepository : GodotResourceRepository<string, VersionedItemData>
{
public VersionedItemRepository()
{
LoadFromPath("res://data/items");
ValidateVersions();
}
private void ValidateVersions()
{
var allItems = GetAll();
foreach (var item in allItems)
{
if (item.Version < 2)
{
GD.PrintErr($"物品 {item.Id} 版本过旧: v{item.Version}");
}
}
}
}
多类型资源管理
// 武器资源
[GlobalClass]
public partial class WeaponData : Resource, IHasKey<string>
{
[Export] public string Id { get; set; }
[Export] public string Name { get; set; }
[Export] public int Damage { get; set; }
public string Key => Id;
}
// 护甲资源
[GlobalClass]
public partial class ArmorData : Resource, IHasKey<string>
{
[Export] public string Id { get; set; }
[Export] public string Name { get; set; }
[Export] public int Defense { get; set; }
public string Key => Id;
}
// 统一管理
public class EquipmentManager
{
private readonly IResourceRepository<string, WeaponData> _weaponRepo;
private readonly IResourceRepository<string, ArmorData> _armorRepo;
public EquipmentManager(
IResourceRepository<string, WeaponData> weaponRepo,
IResourceRepository<string, ArmorData> armorRepo)
{
_weaponRepo = weaponRepo;
_armorRepo = armorRepo;
}
public void ShowAllEquipment()
{
GD.Print("=== 武器 ===");
foreach (var weapon in _weaponRepo.GetAll())
{
GD.Print($"{weapon.Name}: 伤害 {weapon.Damage}");
}
GD.Print("=== 护甲 ===");
foreach (var armor in _armorRepo.GetAll())
{
GD.Print($"{armor.Name}: 防御 {armor.Defense}");
}
}
}
资源热重载
public partial class HotReloadManager : Node
{
private IResourceRepository<string, ItemData> _itemRepo;
public override void _Ready()
{
_itemRepo = this.GetUtility<IResourceRepository<string, ItemData>>();
}
public void ReloadResources()
{
// 清空现有资源
_itemRepo.Clear();
// 重新加载
var repo = _itemRepo as GodotResourceRepository<string, ItemData>;
repo?.LoadFromPath("res://data/items");
GD.Print("资源已重新加载");
}
public override void _Input(InputEvent @event)
{
// 按 F5 热重载
if (@event is InputEventKey keyEvent &&
keyEvent.Pressed &&
keyEvent.Keycode == Key.F5)
{
ReloadResources();
}
}
}
最佳实践
-
资源实现 IHasKey 接口:确保资源可以被仓储管理
✓ public partial class ItemData : Resource, IHasKey<string> { } ✗ public partial class ItemData : Resource { } // 无法使用仓储 -
使用有意义的键类型:根据业务需求选择合适的键类型
✓ IResourceRepository<string, ItemData> // 字符串 ID ✓ IResourceRepository<int, LevelData> // 整数关卡号 ✓ IResourceRepository<Guid, SaveData> // GUID 唯一标识 -
在架构初始化时加载资源:避免运行时加载卡顿
protected override void InstallModules() { var itemRepo = new ItemRepository(); RegisterUtility<IResourceRepository<string, ItemData>>(itemRepo); } -
使用递归加载组织资源:保持目录结构清晰
// 推荐的目录结构 res://data/ ├── items/ │ ├── weapons/ │ ├── armors/ │ └── consumables/ └── enemies/ ├── bosses/ └── minions/ -
处理资源不存在的情况:使用 TryGet 避免异常
✓ if (_itemRepo.TryGet(itemId, out var item)) { } ✗ var item = _itemRepo.Get(itemId); // 可能抛出异常 -
合理使用资源缓存:平衡内存和性能
// 频繁访问的资源可以缓存 private ItemData _cachedPlayerWeapon; public ItemData GetPlayerWeapon() { return _cachedPlayerWeapon ??= _itemRepo.Get("player_weapon"); }
常见问题
问题:如何让资源支持仓储管理?
解答:
资源类必须实现 IHasKey<TKey> 接口:
[GlobalClass]
public partial class MyResource : Resource, IHasKey<string>
{
[Export]
public string Id { get; set; }
public string Key => Id;
}
问题:资源文件必须是什么格式?
解答:
资源仓储支持 Godot 的 .tres 和 .res 文件格式:
.tres:文本格式,可读性好,适合版本控制.res:二进制格式,加载更快,适合发布版本
问题:如何组织资源目录结构?
解答: 推荐按类型和功能组织:
res://data/
├── items/ # 物品资源
│ ├── weapons/
│ ├── armors/
│ └── consumables/
├── enemies/ # 敌人资源
├── skills/ # 技能资源
└── levels/ # 关卡资源
问题:资源加载会阻塞主线程吗?
解答:
LoadFromPath 是同步操作,建议在游戏初始化时加载:
public override void _Ready()
{
// 在游戏启动时加载
var itemRepo = new ItemRepository();
RegisterUtility(itemRepo);
}
问题:如何处理重复的资源键?
解答: 仓储会记录警告但不会抛出异常:
// 日志会显示: "Duplicate key detected: item_001"
// 后加载的资源会被忽略
问题:可以动态添加和移除资源吗?
解答:
可以,使用 Add 和 Remove 方法:
// 添加资源
_itemRepo.Add("new_item", newItemData);
// 移除资源
_itemRepo.Remove("old_item");
// 清空所有资源
_itemRepo.Clear();
问题:如何实现资源的延迟加载?
解答: 可以创建包装类实现延迟加载:
public class LazyResourceRepository<TKey, TResource>
where TResource : Resource, IHasKey<TKey>
where TKey : notnull
{
private GodotResourceRepository<TKey, TResource> _repository;
private readonly string[] _paths;
private bool _loaded;
public LazyResourceRepository(params string[] paths)
{
_paths = paths;
}
private void EnsureLoaded()
{
if (_loaded) return;
_repository = new GodotResourceRepository<TKey, TResource>();
_repository.LoadFromPath(_paths);
_loaded = true;
}
public TResource Get(TKey key)
{
EnsureLoaded();
return _repository.Get(key);
}
}
相关文档
- 数据与存档系统 - 数据持久化
- Godot 架构集成 - Godot 架构基础
- Godot 场景系统 - 场景资源管理
- 资源管理系统 - 核心资源管理