无望其速成,无诱于势利。
一、展示
在 【极简】Godot4.4 快速搭建测试场景A(附完整代码/素材包)基础上,本文实现简易仓库,功能:物品堆叠。
二、复刻
(一)节点结构
1. inventory_ui.tscn
- InventoryUI(CanvasLayer)
- PanelContainer
- GridContainer
- PanelContainer
2. slot_ui.tscn
- SlotUI(VBoxContainer)
- TextureRect
- StackLabel(Label)
- NameLabel(Label)
- TextureRect
(二)独立资源/脚本
1. event_bus.gd(事件总线)
功能:集中定义信号
extends Node
@warning_ignore("unused_signal")
signal inventory_item_added(item: InventoryItem) # 添加物品
var inventory_items: Array[Resource] # 存储物品数据的列表
func _ready() -> void:
inventory_items.resize(6)
项目设置->全局->路径(该脚本的路径)->添加,使得该脚本被设置为全局脚本。
2. inventory_item.gd(物品信息模板)
功能:可复用的物品信息的模板
class_name InventoryItem
extends Resource
@export var texture: Texture
@export var name: String
@export var max_stack: int = 99
var stack: int = 0
3. inventory_item.tres(物体信息实体)
创建resource文件,并命名为 inventory_item.tres;
点击打开后,在检查中把inventory_item.gd 附加到 inventory_item.tres 上;
可以发现检查器中多出来InventoryItem,里面正是在 inventory_item.gd 中的各种数值。
(三)附加脚本
1. Egg.gd
附加到egg.tscn中的Egg节点(Sprite2D):
extends Sprite2D
## 可收集的蛋
@export var item: InventoryItem
# 蛋的默认数量
@export var stack : int = 1
# 从PickUpComponent连接的信号
func _on_pick_up_component_body_entered(body: Node2D) -> void:
if body is CharacterBody2D:
item.stack = stack
EventBus.inventory_item_added.emit(item) # 蛋被收集后发送广播信号
queue_free() # 删除蛋
2. inventory_ui.gd
附加到 inventory_ui.tscn中的InventoryUI节点(CanvasLayer):
class_name InventoryUI
extends CanvasLayer
## 库存面板,用于更新物品槽,处理事件
const SLOT_UI = preload("res://scenes/UI/slot_ui.tscn")
@export var sizes: int = 6 # 物品槽的总数
@export var columns: int = 3 # 物品槽的列数
@onready var grid_container: GridContainer = $PanelContainer/GridContainer
func _ready() -> void:
# 在库存面板中初始化所有的物品槽
grid_container.columns = columns
for i in sizes:
var slot = SLOT_UI.instantiate()
slot.slot_index = i
grid_container.add_child(slot)
# 连接添加物品信号
EventBus.inventory_item_added.connect(_on_egg_inventory_item_added)
# 当收到egg节点发送的信号inventory_item_added后,更新库存面板
# 命名规范:_on_[发送节点名]_[发送信号名]
func _on_egg_inventory_item_added(item: InventoryItem) ->void:
# TODO: 先堆叠同名物品,若有剩余物品,再寻空物品槽加入
var remain: int = item.stack
var index: int = -1
# 寻找同名物品,若存在且有空位进行堆叠
for i in EventBus.inventory_items.size():
if EventBus.inventory_items[i] != null and EventBus.inventory_items[i].name == item.name and remain > 0:
var available: int = EventBus.inventory_items[i].max_stack - EventBus.inventory_items[i].stack
index = i
EventBus.inventory_items[i].stack += min(remain, available) # 堆叠物品
# 更新原本的slot标签
update_slot_stack_label(EventBus.inventory_items[i], index)
remain = remain - available #剩余未堆叠物品
# 将未堆叠的剩余物品放进空物品槽(如果有的话)
while remain > 0 and EventBus.inventory_items.has(null):
# 放进空物品槽
var add_mount:int = min(remain, item.max_stack)
var new_item:InventoryItem = item.duplicate(true) #创建一个新的item
new_item.stack = add_mount
var first_empty_index: int = EventBus.inventory_items.find(null)
EventBus.inventory_items[first_empty_index] = new_item# 保存进列表
update_first_empty_slot(new_item) # 选择第一个空槽,更新物品信息
# 剩余物品若大于零,继续循环找空位放置
remain = remain - add_mount
# 放进从左往右,从上到下数第一个空槽
func update_first_empty_slot(item: InventoryItem) ->void:
# 过滤出当前所有的空槽
var empty_slots: Array = grid_container.get_children().filter(func(slot): return slot.is_empty)
#选择空槽列表中的第一个
var first_empty_slot :SlotUI = empty_slots.front()
#创建一个空格
first_empty_slot.update_slot(item)
# 跟新物品槽数量标签
func update_slot_stack_label(item: InventoryItem, index: int) ->void:
grid_container.get_child(index).stack_label.text = str(item.stack)
如果 const SLOT_UI = preload(“res://slot_ui.tscn”) 报错,说明路径不对,需改成文件系统中自定义的slot_ui.tscn所在的路径。
3. slot_ui.gd
附加到 slot_ui.tscn 中的 SlotUI 节点(VBoxContainer):
class_name SlotUI
extends VBoxContainer
## 物品槽,实现拖放
@export var inventory_item: InventoryItem
var is_empty: bool = true #物品槽默认为空
var slot_index: int = -1 #物品槽索引默认为-1
@onready var texture_rect: TextureRect = $TextureRect
@onready var stack_label: Label = $TextureRect/StackLabel
@onready var name_label: Label = $NameLabel
# 初始化物品槽
func _ready() -> void:
if inventory_item.texture != null:
texture_rect.texture = inventory_item.texture
if inventory_item.name != null:
name_label.text = inventory_item.name
# 当收到InventoryUI节点发送的信号后,更新物品槽
# 命名规范:_on_[发送节点名]_[发送信号名]
func update_slot(item: InventoryItem) ->void:
texture_rect.texture = item.texture
stack_label.text = str(item.stack)
name_label.text = item.name
is_empty = false
4. slot_ui.tscn
选中场景中的SlotUI节点,在检查器中加载物品信息模板 inventory_item.tres;
5. egg.tscn
首先我们找到 inventory_item.tres 文件,将它复制为 egg_inventory_item.tres;
选中 egg.tscn 场景中的 Egg 节点,在检查器中加载物品信息模板 egg_inventory_item.tres;
并在Texture、Name中分别填上蛋的图片和名字信息;
(四)其他设置
1. inventory_ui.tscn
选中 inventory_ui.tscn 场景下的 PanelContainer 节点,在检查器Layout->Custom Minimum Size设置x = 90.0px, y = 76.0px,然后找到锚点预设,将当前节点设置为居中;
2. slot_ui.tscn
选中 slot_ui.tscn 场景下的 TextureRect 节点,在检查器Layout->Custom Minimum Size设置x = 30.0px, y = 30.0px;
选中 slot_ui.tscn 场景下的 StackLabel 节点,在检查器中设置为右下方向;
选中 slot_ui.tscn 场景下的 StackLabel 节点在检查器Control->Transform:Size.x= 30.0px,Size.y = 30.0px;
选中 slot_ui.tscn 场景下的 StackLabel 节点在检查器Control->Theme中新建Theme,然后将默认字体大小 Default Font Size 设置为10px;
在Theme Overrides下勾选字体阴影颜色 Font Shadow Color 和字体轮廓颜色 Font Outline Color;
同样,选中slot_ui.tscn 场景下的 NameLabel 节点在检查器设置如下图所示;
3. 显示画面太小
在项目设置->显示->窗口:缩放设置为4.0;
4. 画面模糊
在项目设置->渲染->纹理->画布纹理:默认纹理过滤设置为Nearest;
三、运行测试
搭建测试场景如下:
四、自查对照
问题现象 | 关键检查点 | 解决方案 |
---|---|---|
The InputMap action “down” doesn’t exist. Did you mean “ui_down”? | 检查输入映射是否存在left ,right ,up ,down 动作 |
项目设置→输入映射→添加left ,right ,up ,down 并绑定键盘左、右键 |
画面太小 | 检查项目设置->常规->显示->拉伸->缩放 | 调整缩放大小 |
角色像素纹理不清晰 | 检查项目设置->渲染->纹理->画布纹理->默认纹理过滤 | 在默认纹理过滤设置为Nearest |
Invalid assignment of property or key ‘stack’ with value of type ‘int’ on a base object of type ‘Nil’. | egg.tscn场景或者是slot_ui.tscn场景未加载egg_inventory_item.tres或inventory_item.tres文件 | 打开场景在检查器中加载对应文件 |
库存面板在左上角 | 检查inventory_ui.tscn场景中的PanelContainer是否预设未居中 | 选中inventory_ui.tscn场景中的PanelContainer节点,在检查器或者是2D区预设该节点为居中 |
(欢迎评论区讨论!)
五、免费开源资产包
某开源网站精灵图资源包链接: 点击此处
-
进入链接后点击下图按钮
-
然后点击【No thanks,just take me to the downloads】(不了谢谢,只想下载)
-
最后点击下图按钮完成下载(注意导入前需解压缩)