重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
godot-inventory-system by thedivergentai/gd-agentic-skills
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-inventory-system槽位管理、堆叠逻辑和基于资源的物品定义了健壮的库存系统。
所有库存物品的基础资源,允许序列化的 .tres 物品数据库。
用于单个库存槽位的响应式数据结构,将变更广播到用户界面。
用于管理库存数组、堆叠逻辑和查找空槽位的集中式资源。
基于网格的用户界面控制器,使用"响应式用户界面"模式将 InventoryData 映射到可视槽位。
用于移动和交换库存物品的原生 Godot 拖放实现。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
全局注册表模式,用于通过唯一 ID 字符串高效加载和查找物品。
用于将复杂库存结构序列化和反序列化到磁盘的专家级逻辑。
通过继承实现特定物品行为(药水、食物)的扩展模式。
用于随机掉落和宝箱内容的数据驱动战利品分布定义。
将物理 2D/3D 拾取物转换为库存数据的世界空间桥梁。
Item extends Node 会导致场景树急剧膨胀和内存泄漏。始终使用 Item extends Resource 以获得轻量级数据 [20]。float 表示物品数量 — 浮点误差(例如 0.9999 而不是 1)会破坏你的"等于零"检查。坚持使用 int 表示数量 [23]。inventory_updated 信号 [25]。ItemDatabase 来查找资源。这对于存档系统兼容性至关重要。is_instance_valid() — 如果槽位的物品为空,尝试访问 .icon 会导致用户界面崩溃。InventoryData 资源。_process() 循环内创建新的 Resource 实例 — 预先实例化你的库存槽位或重用现有槽位,以防止分配峰值。# item.gd (Resource)
class_name Item
extends Resource
@export var id: String
@export var display_name: String
@export var icon: Texture2D
@export var max_stack: int = 1
@export var weight: float = 0.0
@export_multiline var description: String
# inventory.gd
class_name Inventory
extends Resource
signal item_added(item: Item, amount: int)
signal item_removed(item: Item, amount: int)
signal inventory_changed
@export var slots: Array[InventorySlot] = []
@export var max_slots: int = 20
@export var max_weight: float = 100.0
func _init() -> void:
slots.resize(max_slots)
for i in max_slots:
slots[i] = InventorySlot.new()
func add_item(item: Item, amount: int = 1) -> bool:
var remaining := amount
# 首先尝试堆叠
if item.max_stack > 1:
for slot in slots:
if slot.item == item and slot.amount < item.max_stack:
var space := item.max_stack - slot.amount
var to_add := mini(space, remaining)
slot.amount += to_add
remaining -= to_add
if remaining <= 0:
item_added.emit(item, amount)
inventory_changed.emit()
return true
# 添加到空槽位
while remaining > 0:
var empty_slot := find_empty_slot()
if empty_slot == null:
return false # 库存已满
var to_add := mini(item.max_stack, remaining)
empty_slot.item = item
empty_slot.amount = to_add
remaining -= to_add
item_added.emit(item, amount)
inventory_changed.emit()
return true
func remove_item(item: Item, amount: int = 1) -> bool:
var remaining := amount
for slot in slots:
if slot.item == item:
var to_remove := mini(slot.amount, remaining)
slot.amount -= to_remove
remaining -= to_remove
if slot.amount <= 0:
slot.clear()
if remaining <= 0:
item_removed.emit(item, amount)
inventory_changed.emit()
return true
return false # 物品数量不足
func has_item(item: Item, amount: int = 1) -> bool:
var count := 0
for slot in slots:
if slot.item == item:
count += slot.amount
return count >= amount
func find_empty_slot() -> InventorySlot:
for slot in slots:
if slot.is_empty():
return slot
return null
func get_total_weight() -> float:
var total := 0.0
for slot in slots:
if slot.item:
total += slot.item.weight * slot.amount
return total
# inventory_slot.gd
class_name InventorySlot
extends Resource
signal slot_changed
var item: Item = null
var amount: int = 0
func is_empty() -> bool:
return item == null
func clear() -> void:
item = null
amount = 0
slot_changed.emit()
# equipment.gd
class_name Equipment
extends Resource
signal equipment_changed(slot: String, item: Item)
@export var weapon: Item = null
@export var armor: Item = null
@export var accessory: Item = null
func equip(slot: String, item: Item) -> Item:
var old_item: Item = null
match slot:
"weapon":
old_item = weapon
weapon = item
"armor":
old_item = armor
armor = item
"accessory":
old_item = accessory
accessory = item
equipment_changed.emit(slot, item)
return old_item
func unequip(slot: String) -> Item:
return equip(slot, null)
func get_total_stats() -> Dictionary:
var stats := {
"attack": 0,
"defense": 0,
"speed": 0
}
for item in [weapon, armor, accessory]:
if item and item.has("stats"):
for key in item.stats:
stats[key] += item.stats[key]
return stats
# inventory_ui.gd
extends Control
@onready var grid := $GridContainer
var inventory: Inventory
func _ready() -> void:
inventory.inventory_changed.connect(refresh_ui)
refresh_ui()
func refresh_ui() -> void:
# 清除现有内容
for child in grid.get_children():
child.queue_free()
# 创建槽位用户界面
for slot in inventory.slots:
var slot_ui := InventorySlotUI.new()
slot_ui.setup(slot)
grid.add_child(slot_ui)
# crafting_recipe.gd
class_name CraftingRecipe
extends Resource
@export var result: Item
@export var result_amount: int = 1
@export var requirements: Array[CraftingRequirement]
func can_craft(inventory: Inventory) -> bool:
for req in requirements:
if not inventory.has_item(req.item, req.amount):
return false
return true
func craft(inventory: Inventory) -> bool:
if not can_craft(inventory):
return false
# 移除材料
for req in requirements:
inventory.remove_item(req.item, req.amount)
# 添加结果
inventory.add_item(result, result_amount)
return true
func save_inventory() -> Dictionary:
return {
"slots": slots.map(func(s): return s.to_dict())
}
func load_inventory(data: Dictionary) -> void:
for i in data.slots.size():
slots[i].from_dict(data.slots[i])
inventory_changed.emit()
max_stackgodot-save-load-systems, godot-resource-data-patterns每周安装量
70
代码仓库
GitHub 星标数
59
首次出现
2026年2月10日
安全审计
安装于
opencode67
gemini-cli66
codex65
github-copilot65
kimi-cli64
amp64
Slot management, stacking logic, and resource-based items define robust inventory systems.
Base Resource for all inventory items, allowing for serialized .tres item databases.
Reactive data structure for a single inventory slot, broadcasting changes to the UI.
Centralized Resource for managing inventory arrays, stacking logic, and empty slot finding.
Grid-based UI controller that maps InventoryData to visual slots using the "Reactive UI" pattern.
Native Godot drag-and-drop implementation for moving and swapping inventory items.
Global registry pattern to efficiently load and lookup items by unique ID strings.
Expert logic for serializing and deserializing complex inventory structures to disk.
Extension pattern for implementing specific item behaviors (Potions, Food) via inheritance.
Data-driven loot distribution definition for random drops and chest contents.
World-space bridge for converting physical 2D/3D pickups into inventory data.
Item extends Node leads to massive SceneTree bloat and memory leaks. Always use Item extends Resource for lightweight data [20].float for item quantities — Floating point errors (e.g. 0.9999 instead of 1) will break your "equal to zero" checks. Stick to int for counts [23].inventory_updated signal after the loop completes [25].ItemDatabase to look up resources. This is CRITICAL for save system compatibility.# item.gd (Resource)
class_name Item
extends Resource
@export var id: String
@export var display_name: String
@export var icon: Texture2D
@export var max_stack: int = 1
@export var weight: float = 0.0
@export_multiline var description: String
# inventory.gd
class_name Inventory
extends Resource
signal item_added(item: Item, amount: int)
signal item_removed(item: Item, amount: int)
signal inventory_changed
@export var slots: Array[InventorySlot] = []
@export var max_slots: int = 20
@export var max_weight: float = 100.0
func _init() -> void:
slots.resize(max_slots)
for i in max_slots:
slots[i] = InventorySlot.new()
func add_item(item: Item, amount: int = 1) -> bool:
var remaining := amount
# Try stacking first
if item.max_stack > 1:
for slot in slots:
if slot.item == item and slot.amount < item.max_stack:
var space := item.max_stack - slot.amount
var to_add := mini(space, remaining)
slot.amount += to_add
remaining -= to_add
if remaining <= 0:
item_added.emit(item, amount)
inventory_changed.emit()
return true
# Add to empty slots
while remaining > 0:
var empty_slot := find_empty_slot()
if empty_slot == null:
return false # Inventory full
var to_add := mini(item.max_stack, remaining)
empty_slot.item = item
empty_slot.amount = to_add
remaining -= to_add
item_added.emit(item, amount)
inventory_changed.emit()
return true
func remove_item(item: Item, amount: int = 1) -> bool:
var remaining := amount
for slot in slots:
if slot.item == item:
var to_remove := mini(slot.amount, remaining)
slot.amount -= to_remove
remaining -= to_remove
if slot.amount <= 0:
slot.clear()
if remaining <= 0:
item_removed.emit(item, amount)
inventory_changed.emit()
return true
return false # Not enough items
func has_item(item: Item, amount: int = 1) -> bool:
var count := 0
for slot in slots:
if slot.item == item:
count += slot.amount
return count >= amount
func find_empty_slot() -> InventorySlot:
for slot in slots:
if slot.is_empty():
return slot
return null
func get_total_weight() -> float:
var total := 0.0
for slot in slots:
if slot.item:
total += slot.item.weight * slot.amount
return total
# inventory_slot.gd
class_name InventorySlot
extends Resource
signal slot_changed
var item: Item = null
var amount: int = 0
func is_empty() -> bool:
return item == null
func clear() -> void:
item = null
amount = 0
slot_changed.emit()
# equipment.gd
class_name Equipment
extends Resource
signal equipment_changed(slot: String, item: Item)
@export var weapon: Item = null
@export var armor: Item = null
@export var accessory: Item = null
func equip(slot: String, item: Item) -> Item:
var old_item: Item = null
match slot:
"weapon":
old_item = weapon
weapon = item
"armor":
old_item = armor
armor = item
"accessory":
old_item = accessory
accessory = item
equipment_changed.emit(slot, item)
return old_item
func unequip(slot: String) -> Item:
return equip(slot, null)
func get_total_stats() -> Dictionary:
var stats := {
"attack": 0,
"defense": 0,
"speed": 0
}
for item in [weapon, armor, accessory]:
if item and item.has("stats"):
for key in item.stats:
stats[key] += item.stats[key]
return stats
# inventory_ui.gd
extends Control
@onready var grid := $GridContainer
var inventory: Inventory
func _ready() -> void:
inventory.inventory_changed.connect(refresh_ui)
refresh_ui()
func refresh_ui() -> void:
# Clear existing
for child in grid.get_children():
child.queue_free()
# Create slot UI
for slot in inventory.slots:
var slot_ui := InventorySlotUI.new()
slot_ui.setup(slot)
grid.add_child(slot_ui)
# crafting_recipe.gd
class_name CraftingRecipe
extends Resource
@export var result: Item
@export var result_amount: int = 1
@export var requirements: Array[CraftingRequirement]
func can_craft(inventory: Inventory) -> bool:
for req in requirements:
if not inventory.has_item(req.item, req.amount):
return false
return true
func craft(inventory: Inventory) -> bool:
if not can_craft(inventory):
return false
# Remove ingredients
for req in requirements:
inventory.remove_item(req.item, req.amount)
# Add result
inventory.add_item(result, result_amount)
return true
func save_inventory() -> Dictionary:
return {
"slots": slots.map(func(s): return s.to_dict())
}
func load_inventory(data: Dictionary) -> void:
for i in data.slots.size():
slots[i].from_dict(data.slots[i])
inventory_changed.emit()
max_stack firstgodot-save-load-systems, godot-resource-data-patternsWeekly Installs
70
Repository
GitHub Stars
59
First Seen
Feb 10, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode67
gemini-cli66
codex65
github-copilot65
kimi-cli64
amp64
Three.js 3D Web开发教程 - WebGL/WebGPU图形编程、动画与性能优化指南
551 周安装
Godot MCP 场景构建器 - 高级智能体接口,从零构建 Godot 游戏场景
102 周安装
构建完整AI聊天应用指南:Next.js + Neon + AI SDK实现持久化聊天与自动命名
100 周安装
Deep Research:基于Google Gemini的AI深度研究工具,支持RAG文件上传与自动化报告生成
101 周安装
Gemini Web API 客户端 - 文本/图像生成与多轮对话逆向工程工具
102 周安装
信息图生成器:21种布局×20种风格,AI一键生成专业信息图
102 周安装
AI文章封面图片生成器 - 5维定制,一键生成优雅封面图
103 周安装
is_instance_valid() when accessing item icons — If a slot's item is null, trying to access .icon will crash the UI.InventoryData resource.Resource instances inside a _process() loop — Pre-instantiate your inventory slots or reuse existing ones to prevent allocation spikes.