关于 inventory-system
面向 Godot 4.3+ 开发者的物品栏系统技能,提供 GDScript 和 C# 实现指导。内容涵盖物品与槽位建模、堆叠规则、装备扩展、UI 绑定及存档序列化。
主要功能
- 01以 Resource 定义物品数据
- 02槽位管理与堆叠操作
- 03物品栏及装备信号
- 04网格 UI 与拖放绑定
- 05按物品 ID 序列化存档
适用场景
- 01实现具有堆叠上限的固定槽位背包
- 02将物品栏变化同步到网格 UI
- 03扩展装备槽位并汇总属性
- 04保存槽位内容并在载入时恢复物品
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill inventory-system安装后在 Claude Code 中输入/inventory-system确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/core-classes.mdequipment.mdserialization.mdui-binding.md详细介绍
Godot 4.3+ 中的物品栏系统
所有示例均适用于 Godot 4.3+,不使用已弃用的 API。本技能涵盖相关决策;具体实现位于 references/ 中(先是 GDScript,然后是 C#)——加载所需的实现即可。
**相关技能:**自定义 Resource 数据容器可参阅 resource-pattern,物品栏序列化可参阅 save-load,物品栏变更通知可参阅 event-bus,物品栏 UI 显示可参阅 hud-system,冒险游戏物品栏可参阅 popochiu。
1. 架构概览
┌─────────────────────────────────────────────────────────┐
│ UI Layer │
│ InventoryUI (Control) │
│ └─ GridContainer │
│ └─ SlotUI × N (Button) │
│ └─ TextureRect (icon) + Label (qty) │
│ │
│ Connects to: inventory_changed signal │
│ Drag-and-drop via _get_drag_data / _drop_data │
└───────────────────────┬─────────────────────────────────┘
│ reads / mutates
┌───────────────────────▼─────────────────────────────────┐
│ Inventory (Node) │
│ slots: Array[InventorySlot] │
│ add_item(item, qty) → leftover: int │
│ remove_item(item, qty) │
│ has_item(item, qty) → bool │
│ get_item_count(item) → int │
│ │
│ signals: inventory_changed │
│ item_added(item, quantity) │
│ item_removed(item, quantity) │
└───────────────────────┬─────────────────────────────────┘
│ references
┌───────────────────────▼─────────────────────────────────┐
│ Data Layer (Resources) │
│ ItemData (Resource) │
│ id, name, description, icon, max_stack_size, │
│ item_type enum │
│ │
│ InventorySlot (inner class / Resource) │
│ item: ItemData, quantity: int │
└─────────────────────────────────────────────────────────┘
2. 重要决策
三个选择会影响每个物品栏。请明确做出选择并说明选了哪项——其余工作都是机械性的。
| 决策 | 选择 | 原因 |
|---|---|---|
物品标识:Resource 还是 Dictionary / JSON | .tres 中的 Resource | @export 槽位接受类型化物品,Inspector 可编辑它们,且共享实例按引用比较 |
槽位容器:Resource 还是 RefCounted | RefCounted | 槽位是运行时状态,而非创作资源。使用 Resource 容易导致槽位被保存到磁盘,并重新加载过时的物品副本 |
add_item 返回值:bool 还是剩余数量 | 剩余 int | 部分添加很常见(堆叠上限、背包已满)。使用 bool 会迫使调用方重新查询实际放入的数量 |
如果你的游戏需要其他方案——没有槽位的重量限制背包、可容纳异形物品的网格物品栏、无限堆叠列表——请在开始构建前说明。本文介绍的固定槽位数组不适合作为这三种方案的基础结构。
不易察觉的细节
- 先填满已有堆叠,再填空槽位——按此顺序分两轮处理。简单地遍历一遍会把同一种物品分散到多个槽位,并在仍有空间时错误地报告背包已满。
- 每次操作只触发一次
inventory_changed,并在两轮处理完成后触发——不要每触及一个槽位就触发一次。每个槽位都触发会导致一次add_item让整个 UI 重绘 N 次。 - 物品按引用比较。 Godot 会缓存已加载的资源,因此对同一个
.tres调用load()会返回同一实例,slot.item == item既正确又高效。如果在运行时duplicate()物品,这种做法就会失效——应改用ItemData.id作为键。 - 数量达到 0 时将
item清为null,否则is_empty()会返回错误结果,槽位也无法再次使用。 - 通过数据阻止堆叠,而不是添加分支——将
EQUIPMENT和KEY_ITEM物品的max_stack_size设为1,不要在add_item中为它们添加特殊处理。 - C#:将
ItemData标记为[GlobalClass],否则在创建.tres文件时,Inspector 不会将其作为类型提供。
物品栏 API 约定
| 成员 | 返回值 | 约定 |
|---|---|---|
add_item(item, quantity) | int | 未能放入的剩余数量;0 表示已全部添加 |
remove_item(item, quantity) | — | 最多移除 quantity 个;持有数量不足时不发出通知 |
has_item(item, quantity) | bool | 对 get_item_count 的便捷封装 |
get_item_count(item) | int | 汇总所有槽位中的数量 |
inventory_changed | 信号 | 每次修改操作只触发一次——用于驱动所有 UI |
item_added(item, quantity) | 信号 | 携带实际添加的数量 |
item_removed(item, quantity) | 信号 | 携带实际移除的数量 |
GDScript 和 C# 中完整的
ItemData、Inventory与InventorySlot实现见:references/core-classes.md
3. 装备扩展
在 Inventory 节点旁添加一个 Equipment 节点,并为其添加装备槽位(HEAD、CHEST、WEAPON 等)。Equipment 持有一个以 SlotType 枚举为键的槽位映射。属性汇总会将已装备物品的 stats Dictionary(添加到 ItemData 的属性)相加;槽位变更时触发 equipment_changed 信号。
完整的 GDScript 和 C#
Equipment类(包含SlotType枚举、装备 / 卸下 API 和属性汇总)见 references/equipment.md。
4. UI 绑定
槽位网格 UI:由 Button 槽位控件组成的 GridContainer,每个控件渲染一个 InventorySlot。拖放通过槽位控件上的 _get_drag_data / _drop_data / _can_drop_data 实现。Inventory 发出 inventory_changed;UI 重新渲染受影响的槽位。
完整的 GDScript 和 C# 槽位控件(拖放、悬停预览)、背包网格布局和工具提示接线,请参见 references/ui-binding.md。
5. 序列化
将每个槽位保存为 {id, qty} 键值对(空槽位为 null),不要保存 ItemData 本身或其路径。ItemRegistry 自动加载项会在启动时加载 res://items/,并在加载时将 ID 解析回物品。
有关包含
serialize_inventory/deserialize_inventory的 GDScript 和 C#ItemRegistry,请参见 references/serialization.md;将结果放入存档文件(其version字段请参见 save-load)。
实现检查清单
-
ItemData继承Resource,并在 Inspector 中设置稳定的id字符串 -
ItemData文件存放在res://items/下,并提交到版本控制 -
Inventory.add_item()返回剩余数量;调用方负责处理背包已满的情况 -
inventory_changed信号驱动所有 UI 更新 — UI 绝不逐帧轮询 -
InventorySlot.remove_from_stack()在数量达到 0 时将item清除为null - 装备槽以
SlotType枚举为键,而不是字符串,以便在编译时发现拼写错误 - 需要属性时调用
Equipment.get_total_stat();除非性能分析表明有必要,否则不要缓存结果 - 序列化只存储
id + quantity— 不要存储完整的ItemData对象或资源路径 -
ItemRegistry在启动时加载物品;所有反序列化都通过它进行 - 拖放直接交换槽位内容,然后只发出一次
inventory_changed - 在
EQUIPMENT和KEY_ITEM类型上将max_stack_size = 1,以防止堆叠 - 所有
push_error()消息都包含类名和方法名,便于追踪