Godot 4 Autoload单例模式最佳实践,包含全局管理、事件总线、信号通信。
The English summary is being prepared. Showing the original description.
Godot 4背包系统完整实现,包括数据层、逻辑层和UI层设计。支持物品管理、拖放交互、格子满了处理等常见功能。
The English summary is being prepared. Showing the original description.
See how to installReview the instructions the agent will receive before you install.
完整的背包系统实现,支持物品添加、移除、拖放排序、格子容量管理等功能。
背包系统
├── 数据层 (Data Layer)
│ ├── InventoryItem - 单个物品数据
│ └── InventorySlot - 格子数据(包含物品引用和数量)
├── 逻辑层 (Business Layer)
│ └── InventoryComponent - CRUD操作、堆叠逻辑
└── UI层 (Presentation Layer)
├── InventoryUI - 背包主面板
└── InventorySlotUI - 单个格子UI
InventoryItem、InventorySlot - 纯数据InventorySlotUI - 仅负责显示InventoryComponent - 处理所有业务逻辑# inventory_item.gd
class_name InventoryItem
extends Resource
## 物品唯一标识符
@export var id: StringName
## 物品显示名称
@export var item_name: String
## 物品描述
@export_multiline var description: String
## 最大堆叠数量
@export var max_stack: int = 99
## 物品图标
@export var icon: Texture2D
## 物品 Prefab(可选,用于拖放到场景)
@export var item_scene: PackedScene
## 物品稀有度
@export_enum("Common", "Uncommon", "Rare", "Epic", "Legendary") var rarity: int = 0
## 是否可以丢弃
@export var can_drop: bool = true
## 是否可以堆叠
@export var can_stack: bool = true
## 物品价值(用于商店)
@export var value: int = 1
# inventory_slot.gd
class_name InventorySlot
extends RefCounted
## 格子索引
var index: int
## 格子中的物品
var item: InventoryItem
## 物品数量
var quantity: int = 0
## 格子是否为空
var is_empty: bool:
get: return item == null or quantity <= 0
func can_add(new_item: InventoryItem, amount: int = 1) -> bool:
if is_empty:
return true
if item.id == new_item.id and item.can_stack:
return quantity + amount <= item.max_stack
return false
func add(new_item: InventoryItem, amount: int = 1) -> bool:
if not can_add(new_item, amount):
return false
if is_empty:
item = new_item
quantity = amount
else:
quantity += amount
return true
func remove(amount: int = 1) -> bool:
if is_empty or quantity < amount:
return false
quantity -= amount
if quantity <= 0:
clear()
return true
func clear() -> void:
item = null
quantity = 0
# inventory_component.gd
class_name InventoryComponent
extends Node
## 背包容量
@export var capacity: int = 20
## 信号:物品添加成功
signal item_added(slot_index: int, item: InventoryItem, quantity: int)
## 信号:物品移除成功
signal item_removed(slot_index: int, item: InventoryItem, quantity: int)
## 信号:背包满了
signal inventory_full(item: InventoryItem)
## 信号:格子数据变化
signal slot_changed(slot_index: int)
## 背包格子数组
var slots: Array[InventorySlot] = []
func _ready() -> void:
_initialize_slots()
func _initialize_slots() -> void:
for i in capacity:
var slot := InventorySlot.new()
slot.index = i
slots.append(slot)
## 添加物品到背包
func add_item(item: InventoryItem, quantity: int = 1) -> bool:
# 优先堆叠到已有物品
var target_slot := _find_stackable_slot(item)
if target_slot != null:
var old_qty := target_slot.quantity
target_slot.add(item, quantity)
slot_changed.emit(target_slot.index)
item_added.emit(target_slot.index, item, target_slot.quantity - old_qty)
return true
# 找空格子
var empty_slot := _find_empty_slot()
if empty_slot != null:
empty_slot.add(item, quantity)
slot_changed.emit(empty_slot.index)
item_added.emit(empty_slot.index, item, quantity)
return true
# 背包满了
inventory_full.emit(item)
return false
## 移除物品
func remove_item(slot_index: int, quantity: int = 1) -> bool:
if slot_index < 0 or slot_index >= slots.size():
return false
var slot: InventorySlot = slots[slot_index]
if slot.is_empty:
return false
var item := slot.item
if slot.remove(quantity):
slot_changed.emit(slot_index)
item_removed.emit(slot_index, item, quantity)
return true
return false
## 交换两个格子的物品
func swap_slots(index1: int, index2: int) -> void:
if index1 < 0 or index1 >= slots.size() or index2 < 0 or index2 >= slots.size():
return
var temp_item := slots[index1].item
var temp_qty := slots[index1].quantity
slots[index1].item = slots[index2].item
slots[index1].quantity = slots[index2].quantity
slots[index2].item = temp_item
slots[index2].quantity = temp_qty
slot_changed.emit(index1)
slot_changed.emit(index2)
## 移动物品到另一个背包
func move_to(target: InventoryComponent, slot_index: int, quantity: int = -1) -> bool:
if slot_index < 0 or slot_index >= slots.size():
return false
var slot: InventorySlot = slots[slot_index]
if slot.is_empty:
return false
var transfer_qty := quantity if quantity > 0 else slot.quantity
if target.add_item(slot.item, transfer_qty):
remove_item(slot_index, transfer_qty)
return true
return false
## 查找可堆叠的格子
func _find_stackable_slot(item: InventoryItem) -> InventorySlot:
if not item.can_stack:
return null
for slot in slots:
if not slot.is_empty and slot.item.id == item.id:
if slot.quantity + 1 <= slot.item.max_stack:
return slot
return null
## 查找空格子
func _find_empty_slot() -> InventorySlot:
for slot in slots:
if slot.is_empty:
return slot
return null
## 获取指定物品的数量
func get_item_count(item_id: StringName) -> int:
var total := 0
for slot in slots:
if not slot.is_empty and slot.item.id == item_id:
total += slot.quantity
return total
## 清空背包
func clear() -> void:
for slot in slots:
slot.clear()
slot_changed.emit(slot.index)
## 保存背包数据
func get_save_data() -> Dictionary:
var data := []
for slot in slots:
if not slot.is_empty:
data.append({
"index": slot.index,
"item_id": slot.item.id,
"quantity": slot.quantity
})
return {"slots": data, "capacity": capacity}
## 加载背包数据
func load_save_data(data: Dictionary) -> void:
clear()
if data.has("slots"):
for slot_data in data.slots:
var index: int = slot_data.index
var item_id: StringName = slot_data.item_id
var quantity: int = slot_data.quantity
# 查找物品数据(实际项目中从物品数据库获取)
var item := _get_item_by_id(item_id)
if item != null and index < slots.size():
slots[index].item = item
slots[index].quantity = quantity
slot_changed.emit(index)
# inventory_slot_ui.gd
class_name InventorySlotUI
extends PanelContainer
## 信号:物品被拖走
signal item_drag_started(slot_index: int)
## 信号:物品放置到格子
signal item_dropped(slot_index: int, from_slot: int)
## 信号:右键点击
signal right_clicked(slot_index: int)
@export var slot_index: int = 0
@onready var icon_texture: TextureRect = %IconTexture
@onready var quantity_label: Label = %QuantityLabel
@onready var item_name_label: Label = %ItemNameLabel
var current_item: InventoryItem
var current_quantity: int = 0
var _is_dragging: bool = false
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_STOP
# 初始化为空白
_clear_display()
func set_slot_data(slot: InventorySlot) -> void:
if slot.is_empty:
_clear_display()
return
current_item = slot.item
current_quantity = slot.quantity
_update_display()
func _update_display() -> void:
if current_item == null:
_clear_display()
return
icon_texture.texture = current_item.icon
icon_texture.visible = true
if current_quantity > 1:
quantity_label.text = str(current_quantity)
quantity_label.visible = true
else:
quantity_label.visible = false
item_name_label.text = current_item.item_name
item_name_label.visible = true
func _clear_display() -> void:
current_item = null
current_quantity = 0
icon_texture.texture = null
icon_texture.visible = false
quantity_label.visible = false
item_name_label.visible = false
func _on_mouse_entered() -> void:
if current_item != null:
# 显示物品提示
pass
func _on_gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
_on_left_click()
else:
_on_left_release()
elif event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
right_clicked.emit(slot_index)
func _on_left_click() -> void:
if current_item != null:
item_drag_started.emit(slot_index)
func _on_left_release() -> void:
pass
# inventory_ui.gd
class_name InventoryUI
extends Control
@export var inventory_component: InventoryComponent
@export var slot_scene: PackedScene
@onready var grid_container: GridContainer = %GridContainer
@onready var title_label: Label = %TitleLabel
var slotuis: Array[InventorySlotUI] = []
var _dragged_slot_index: int = -1
var _drag_preview: Control
func _ready() -> void:
assert(inventory_component != null, "InventoryComponent未设置")
inventory_component.slot_changed.connect(_on_slot_changed)
inventory_component.inventory_full.connect(_on_inventory_full)
_create_slot_ui()
_refresh_all_slots()
func _create_slot_ui() -> void:
for i in inventory_component.capacity:
var slot_ui: InventorySlotUI = slot_scene.instantiate()
slot_ui.slot_index = i
slot_ui.item_drag_started.connect(_on_item_drag_started)
slot_ui.item_dropped.connect(_on_item_dropped)
slot_ui.right_clicked.connect(_on_slot_right_clicked)
grid_container.add_child(slot_ui)
slotuis.append(slot_ui)
func _refresh_all_slots() -> void:
for i in inventory_component.slots.size():
slotuis[i].set_slot_data(inventory_component.slots[i])
func _on_slot_changed(slot_index: int) -> void:
if slot_index < slotuis.size():
slotuis[slot_index].set_slot_data(inventory_component.slots[slot_index])
func _on_inventory_full(item: InventoryItem) -> void:
# 显示提示(例如:"背包已满")
print("背包已满,无法添加 ", item.item_name)
func _on_item_drag_started(slot_index: int) -> void:
_dragged_slot_index = slot_index
func _on_item_dropped(slot_index: int, from_slot: int) -> void:
if from_slot != slot_index:
inventory_component.swap_slots(from_slot, slot_index)
func _on_slot_right_clicked(slot_index: int) -> void:
var slot: InventorySlot = inventory_component.slots[slot_index]
if slot.is_empty:
return
# 丢弃物品或显示物品菜单
if slot.item.can_drop:
inventory_component.remove_item(slot_index, 1)
func _input(event: InputEvent) -> void:
if event.is_action_pressed("toggle_inventory"):
visible = not visible
if visible:
_refresh_all_slots()
# item_database.gd (Autoload)
extends Node
@export var items: Array[InventoryItem]
func _ready() -> void:
# 注册所有物品到全局数据库
pass
func get_item(id: StringName) -> InventoryItem:
for item in items:
if item.id == id:
return item
return null
func has_item(id: StringName) -> bool:
return get_item(id) != null
# player.gd
extends CharacterBody2D
@export var inventory: InventoryComponent
func _ready() -> void:
# 给玩家一些初始物品
var sword := item_database.get_item("sword")
var potion := item_database.get_item("health_potion")
if sword:
inventory.add_item(sword, 1)
if potion:
inventory.add_item(potion, 5)
No reviews yet. Tell others what you thought.
No login needed. Share how it worked for you, ask a question, or note where it did not run.
No comments yet. Be the first to write one.
Godot 4 Autoload单例模式最佳实践,包含全局管理、事件总线、信号通信。
The English summary is being prepared. Showing the original description.
Godot 4 Buff系统完整实现,支持状态效果管理、持续时间、循环效果、层叠机制等。使用Resource+Component架构。
The English summary is being prepared. Showing the original description.
Godot 4 建造系统实现,包含GridMap方块管理、第一人称视角、射线交互。
The English summary is being prepared. Showing the original description.
Godot 4 ECS实体组件系统模式,包含组件化设计、数据与逻辑分离、组件通信。
The English summary is being prepared. Showing the original description.
Godot 4 种植系统设计,包含植物数据结构、生长阶段、浇水机制、TileMap应用。
The English summary is being prepared. Showing the original description.
Master Godot 4 GDScript patterns including signals, scenes, state machines, and optimization. Use when building Godot games, implementing game systems, or learning GDScript best practices.
The English summary is being prepared. Showing the original description.