无望其速成,无诱于势利。
一、展示
在 【极简】Godot4.4 简易仓库A(附完整代码/素材包)基础上,本文拓展了原有的仓库功能,实现物品:1. 拖拽放置 2. 拖拽分割
二、复刻
更新脚本
1. event_bus.gd
extends Node
@warning_ignore("unused_signal")
signal inventory_item_added(item: InventoryItem) # 添加物品
@warning_ignore("unused_signal")
signal slot_item_dropped(index: int) # 丢弃物品
@warning_ignore("unused_signal")
signal slot_item_divided(index: int) # 拖拽部分物品
var inventory_items: Array[Resource] # 存储物品数据的列表
func _ready() -> void:
inventory_items.resize(6)
2. slot_ui.gd
class_name SlotUI
extends VBoxContainer
## 物品槽,实现拖放
@export var inventory_item: InventoryItem
var is_empty: bool = true #物品槽默认为空
var slot_index: int = -1 #物品槽索引默认为-1
var current_stack_label_text: String
var is_drag_failed: bool = false #判断拖放是否成功
@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
EventBus.slot_item_dropped.connect(_on_self_slot_item_dropped)
# 当收到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
# 返回true:可放,false:不可放
func _can_drop_data(_at_position: Vector2, _data: Variant) -> bool:
return true
# 拖
func _get_drag_data(_at_position: Vector2) -> Variant:
# 验非空,创建拖动预览图,并发送数据
if !is_empty:
var drag_preview = TextureRect.new()
# 【CTRL + 鼠标左键】拾取物品槽中一半物品
if Input.is_key_pressed(KEY_CTRL):
# 预览图复制体,确保原物品槽中有图形
var drag_pre = drag_preview.duplicate(true)
drag_pre.texture = texture_rect.texture
set_drag_preview(drag_pre)
# 改变原物品槽标签,如果拖动则减半
current_stack_label_text = stack_label.text # 存储原标签
var split_stack_label_text: String = str(ceili(int(stack_label.text)/2.0)) # 除以2向上取整
stack_label.text = str(int(stack_label.text) - int(split_stack_label_text))
is_drag_failed = true
# 发送数据给_can_drop_data和_drop_data
return {
"item": inventory_item,
"texture": texture_rect.texture,
"name": name_label.text,
"stack": split_stack_label_text,
"is_empty": is_empty,
"index": slot_index,
"slot": self,
}
else:
drag_preview.texture = texture_rect.texture
set_drag_preview(drag_preview)
# 发送数据给_can_drop_data和_drop_data
return {
"item": inventory_item,
"texture": texture_rect.texture,
"name": name_label.text,
"stack": stack_label.text,
"is_empty": is_empty,
"index": slot_index,
"slot": self,
}
return null
# 放
func _drop_data(_at_position: Vector2, data: Variant) -> void:
# 如果源物品数据不为空,且放置的对象不为它本身
if data != null and data["slot"] != self:
# 存储目标物品数据
var temp_item: InventoryItem = inventory_item
var temp_texture: Texture = texture_rect.texture
var temp_stack: String = stack_label.text
var temp_name: String = name_label.text
var temp_is_empty: bool = is_empty
var remain: int = int(data["stack"])
# 同名则尝试堆叠
if name_label.text == data["name"]:
var available: int = inventory_item.max_stack - int(stack_label.text)
var add_amount: int = min(available, remain)
var new_amount: int = int(stack_label.text) + add_amount
# 更新目标同名物品堆叠后的信息
update_slot_item(data["item"], data["texture"], str(new_amount), data["name"], data["is_empty"], slot_index)
remain = remain - available
if remain > 0:
# Ctrl+鼠标左键
if data["slot"].is_drag_failed == true:
var update_amount: int = int(data["slot"].current_stack_label_text) - int(data["stack"]) + remain
print(update_amount)
data["slot"].update_slot_item(data["item"], data["texture"], str(update_amount), data["name"], data["is_empty"], data["index"])
print_debug("同名/remain>0/Ctrl+鼠标左键:")
# 鼠标左键
else:
data["slot"].update_slot_item(data["item"], data["texture"], str(remain), data["name"], data["is_empty"], data["index"])
else:
# Ctrl+鼠标左键
if data["slot"].is_drag_failed == true:
# 更新源物品槽物品信息
var update_amount: int = int(data["slot"].current_stack_label_text) - int(data["stack"])
data["slot"].update_slot_item(temp_item, temp_texture, str(update_amount), temp_name, temp_is_empty, data["index"])
data["slot"].is_drag_failed = false
# 鼠标左键
else:
# 清空源物品槽
data["slot"].update_slot_item(null, null, "", "", true, data["index"])
# 空
elif name_label.text == "":
# Ctrl+鼠标左键
if data["slot"].is_drag_failed == true:
if int(data["stack"]) == 1:
return
# 把这一半物品放在空槽中
update_slot_item(data["item"], data["texture"], data["stack"], data["name"], data["is_empty"], slot_index)
# 更新失去这一半物品的源物品槽
var update_amount: int = int(data["slot"].current_stack_label_text) - int(data["stack"])
data["slot"].update_slot_item(temp_item, data["texture"], str(update_amount), data["name"], data["is_empty"], data["index"])
data["slot"].is_drag_failed = false
# 鼠标左键
else:
# 交换物品信息
update_slot_item(data["item"], data["texture"], data["stack"], data["name"], data["is_empty"], slot_index)
data["slot"].update_slot_item(temp_item, temp_texture, temp_stack, temp_name, temp_is_empty, data["index"])
print_debug(EventBus.inventory_items)
print_debug(temp_texture, temp_is_empty)
# 不同名
else:
# Ctrl+鼠标左键
if data["slot"].is_drag_failed == true:
return
# 鼠标左键
else:
# 交换物品信息
update_slot_item(data["item"], data["texture"], data["stack"], data["name"], data["is_empty"], slot_index)
data["slot"].update_slot_item(temp_item,temp_texture, temp_stack, temp_name, temp_is_empty, data["index"])
# 设置拖放进来的物品数据
func update_slot_item(item: InventoryItem, texture: Texture, stack: String, object_name: String, empty: bool, index: int):
# 更新库存数据
texture_rect.texture = texture
stack_label.text = stack
name_label.text = object_name
is_empty = empty
slot_index = index
# 更新全局列表Array[inventory_items]的数据
# 若空则使得该列表元素为空
if is_empty:
EventBus.inventory_items[slot_index] = null
return
# 若不为空,更新列表
else:
var new_item: InventoryItem = item.duplicate(true)
new_item.texture = texture
new_item.stack = int(stack)
new_item.name = object_name
EventBus.inventory_items[slot_index] = new_item
# 丢弃物品
func _on_self_slot_item_dropped(drop_index: int) ->void:
# 清空列表和物品槽
update_slot_item(null, null,"", "", true, drop_index)
# 可扩展丢弃动画
# 如果按Ctrl拖拽失败,没有发生物品信息的变化,则恢复被分割的原标签
func _notification(what: int) -> void:
if what == NOTIFICATION_DRAG_END:
if is_drag_failed:
stack_label.text = current_stack_label_text
is_drag_failed = false
三、运行测试
搭建测试场景如下:
测试完成!
四、免费开源资产包
某开源网站精灵图资源包链接: 点击此处
-
进入链接后点击下图按钮
-
然后点击【No thanks,just take me to the downloads】(不了谢谢,只想下载)
-
最后点击下图按钮完成下载(注意导入前需解压缩)
扫描二维码关注公众号,回复: 17788158 查看本文章