无望其速成,无诱于势利。
一、展示
在 【极简】Godot4.4 简易仓库A(附完整代码/素材包)与【极简】Godot4.4 简易仓库B(附完整代码/素材包)基础上,本文拓展了原有的仓库功能,实现物品:1. 鼠标左键丢弃物品 2.Ctrl + 鼠标左键丢弃物品
二、复刻
更新脚本
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 #判断拖放是否成功
var is_ctrl: bool = false # 判断Ctrl是否成功
var is_selected: 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:
is_selected = true # 被选中
# 验非空,创建拖动预览图,并发送数据
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 # 默认失败
is_ctrl = true # Ctrl操作
# 发送数据给_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("Ctrl将物品放到同名物品槽中,且溢出", EventBus.inventory_items)
# 鼠标左键
else:
data["slot"].update_slot_item(data["item"], data["texture"], str(remain), data["name"], data["is_empty"], data["index"])
print_debug("左键将物品放到同名物品槽中,且溢出", EventBus.inventory_items)
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
print_debug("Ctrl将物品放到同名物品槽中,且不溢出", EventBus.inventory_items)
# 鼠标左键
else:
# 清空源物品槽
data["slot"].update_slot_item(null, null, "", "", true, data["index"])
print_debug("左键将物品放到同名物品槽中,且不溢出", EventBus.inventory_items)
# 空
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
print_debug("Ctrl将物品放到空槽中:", EventBus.inventory_items)
# 鼠标左键
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)
# 不同名
elif name_label.text != data["slot"].name:
# Ctrl+鼠标左键
if data["slot"].is_drag_failed == true:
print_debug("Ctrl将物品放到不同名物品槽中,不处理", EventBus.inventory_items)
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"])
print_debug("Ctrl将物品放到不同名物品槽中,交换物品", EventBus.inventory_items)
# 设置拖放进来的物品数据
func update_slot_item(item: InventoryItem, texture: Texture, stack: String, object_name: String, empty: bool, index: int):
if int(stack) == 0 || empty:
#清空物品槽UI显示
texture_rect.texture = null
stack_label.text = ""
name_label.text = ""
is_empty = true
slot_index = index
# 清空全局列表数据
EventBus.inventory_items[index] = null
return
else:
# 更新物品槽UI显示
texture_rect.texture = texture
stack_label.text = stack
name_label.text = object_name
is_empty = empty
slot_index = index
# 更新全局列表数据
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:
# 清空列表和物品槽
# 确定是正确的物品槽
if slot_index == drop_index:
# 确定是Ctrl
if is_ctrl:
# 移除丢弃的部分,未丢弃的部分依旧在物品槽中
# 更新源物品槽数量即可
#保持更改
if int(inventory_item.stack) == 0:
EventBus.inventory_items[slot_index] = null
else:
return
# 确定是左键
else:
# 左键丢弃
update_slot_item(null, null,"", "", true, drop_index)
# 可扩展丢弃动画
# 如果按Ctrl拖拽失败,没有发生物品信息的变化,则恢复被分割的原标签
func _notification(what: int) -> void:
if what == NOTIFICATION_DRAG_END and is_selected:
var mouse_global_pos = get_viewport().get_mouse_position()
var panel_rect = get_parent().get_parent().get_global_rect()
is_selected = false
# 处理Ctrl拖拽失败
if is_drag_failed and is_ctrl:
# 失败处理1:超出区域
if not panel_rect.has_point(mouse_global_pos):
# TODO:丢弃操作
print_debug("Ctrl丢弃了")
EventBus.slot_item_dropped.emit(slot_index)
print_debug(EventBus.inventory_items)
# 恢复状态
is_drag_failed = false
is_ctrl = false
return
# 失败处理2:其他情况
else:
# 更新物品数量标签
stack_label.text = current_stack_label_text
# 恢复状态
is_drag_failed = false
is_ctrl = false
return
# 处理鼠标左键拖拽失败
# 如果为鼠标左键拖拽,且超出范围则丢弃,否则不处理
elif not is_ctrl:
if not panel_rect.has_point(mouse_global_pos):
# TODO:丢弃操作
print_debug("左键丢弃了")
# 所有的slot都收到了信号,导致所有物品被删除
EventBus.slot_item_dropped.emit(slot_index)
print_debug(EventBus.inventory_items)
return
# 处理Ctrl拖拽成功,重置is_ctrl
else:
is_ctrl = false
三、运行测试
鼠标左键、Ctrl+鼠标左键操作测试是否丢弃成功:
测试完成!
四、免费开源资产包
某开源网站精灵图资源包链接: 点击此处
-
进入链接后点击下图按钮
-
然后点击【No thanks,just take me to the downloads】(不了谢谢,只想下载)
-
最后点击下图按钮完成下载(注意导入前需解压缩)