【极简】Godot4.4 简易仓库B(附完整代码/素材包)

无望其速成,无诱于势利。

一、展示

【极简】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

三、运行测试

搭建测试场景如下:

在这里插入图片描述
请添加图片描述
测试完成!

四、免费开源资产包

某开源网站精灵图资源包链接: 点击此处

  1. 进入链接后点击下图按钮
    下载

  2. 然后点击【No thanks,just take me to the downloads】(不了谢谢,只想下载)
    No thanks,just take me to the downloads

  3. 最后点击下图按钮完成下载(注意导入前需解压缩)
    下载

    扫描二维码关注公众号,回复: 17788158 查看本文章