Hallo allerseits, mein Name ist Vyacheslav und ich bin Programmierer, aber speziell bin ich jetzt mit der Spieleentwicklung auf GodotEngine beschĂ€ftigt und gleichzeitig betreibe ich meinen Telegrammkanal, in dem ich Notizen zum Erstellen meines eigenen Spiels auf dieser Engine und schreibe Geben Sie Neulingen Material fĂŒr das Studium von Godot.
Kommen wir nun zur Sache. Warum sollten wir mit Drag & Drop und einem Bonus von mir ein einfaches Inventar erstellen?
Lasst uns beginnen. Ich bin kein Designer, daher wird es eine funktionale Version geben. Machen Sie es spÀter selbst.
Zuerst erstellen wir ein Projekt und fĂŒgen die fĂŒr die Arbeit erforderlichen Knoten in der Mindestversion hinzu:
Wir werfen den PanelContainer in das Steuerelement, strecken ihn ĂŒber die SchaltflĂ€che Layout (Ansicht) ĂŒber das gesamte Steuerelement und werfen sofort Flaggen auf die Erweiterung in Höhe und Breite:
Wir werfen einen GridContainer (Grid) mit einem Kind hinein, wir werfen bereits unsere Elemente hinein und zum bequemen Debuggen fĂŒgen wir eine SchaltflĂ€che hinzu, um das Element âaufzunehmenâ. Es wird ein zufĂ€lliges Element mit einem Zufall erzeugt Nummer.
Wir werden 8 Spalten im Inventar und 4 Zeilen haben, fĂŒr die notwendige Vielfalt habe ich Artikelsymbole vorbereitet.
Laden Sie die Schriftart von Google herunter und legen Sie sie im Steuerelement ab, damit wir die SchriftgröĂe Ă€ndern können:
Lassen Sie es uns als NĂ€chstes ein wenig so gestalten, dass es eher wie ein Inventar aussieht, erstellen Sie einen Steckplatz und speichern Sie es seitdem in einer separaten Datei Wir werden dynamisch Slots erstellen:
Als nÀchstes werfen wir das folgende Skript in die Hauptszene:
extends Control
export (int, 1, 20) var columns = 8
export (int, 1, 20) var rows = 4
onready var inv = $InvContainer/InvContent
const slot_scene = preload("res://Slot.tscn")
func _ready():
inv.columns = columns
for i in range(columns*rows):
var slot = slot_scene.instance()
inv.add_child(slot)
Eine Zwischenoption ist ungefÀhr so:
, , , TextureRect Label - :
, , , , :
:
Slot , :
extends PanelContainer
onready var item = $Item
onready var icon = $Item/Icon
onready var count = $Item/Count
var item_type = null
var item_count = 0
func _ready():
update_data({"type": "item_type_1", "count": 0})
func update_data(data = null):
item.visible = data != null
if data:
icon.texture = load("res://graphics/%s.png" % data.type) #
count.text = str(data.count)
:
:
:
extends Control
export (int, 1, 20) var columns = 8
export (int, 1, 20) var rows = 4
onready var inv = $InvContainer/InvContent
const slot_scene = preload("res://Slot.tscn")
func _ready():
$InvContainer/HBoxContainer/Clear.connect("pressed", self, "clear_inventory")
inv.columns = columns
for i in range(columns*rows):
var slot = slot_scene.instance()
inv.add_child(slot)
func clear_inventory():
for child in inv.get_children(): #
child.update_data() #
, .
.
:
extends PanelContainer
onready var item = $Item
onready var icon = $Item/Icon
onready var count = $Item/Count
var item_data = null
func _ready():
update_data()
func empty():
return item_data == null
func update_data(data = null):
item.visible = data != null
item_data = data
if item:
icon.texture = load("res://graphics/%s.png" % item_data.type) #
count.text = str(item_data.count)
return true
:
func has_empty_slot(): #
for child in inv.get_children(): #
if child.empty():
return true
return false
func get_empty_slot(): #
var slot = null
if has_empty_slot():
# ,
#
while slot == null: # ,
var temp_slot = inv.get_child(rng.randi_range(0, columns*rows-1))
if temp_slot.empty():
slot = temp_slot
break
return slot
func add_item(): # ,
var slot = get_empty_slot()
if slot:
var data = {"type":"", "count": 0}
data.type = "item_type_" + str(rng.randi_range(1, 8))
data.count = rng.randi_range(1, 999)
slot.update_data(data)
âadd_itemâ, .
D&D(Drag&Drop).
, , .. .
:
, , .
extends PanelContainer
onready var icon = $Icon
onready var count = $Count
const path_to_items_icons = "res://graphics/%s.png"
func set_data(item_data):
icon.texture = load(path_to_items_icons % item_data.type) #
count.text = str(item_data.count)
:
, âNumâ, , , . , :
, ( ), )
, , , :
extends Control
export (int, 1, 20) var columns = 8 #-
export (int, 1, 20) var rows = 4 #-
const slot_scene = preload("res://Slot.tscn") #
onready var inv = $InvContainer/InvContent #
onready var titem = $TempItem # ,
onready var rng = RandomNumberGenerator.new() #
onready var item_dragging = null #
onready var prev_slot = null #
func ready():
titem.visible = false #
rng.randomize() #
$InvContainer/HBoxContainer/Clear.connect("pressed", self, "clear_inventory")
$InvContainer/HBoxContainer/Add.connect("pressed", self, "add_item")
inv.columns = columns # -
for i in range(columns*rows): #
var slot = slot_scene.instance() #
slot.name = "Slot%d" % i # , ,
slot.get_node("Num").text = str(i) # ,
,
inv.add_child(slot) #
func clear_inventory(): #
for child in inv.get_children(): #
child.update_data() #
func has_empty_slot(): #
for child in inv.get_children(): #
if child.empty():
return true
return false
func get_empty_slot(): #
var slot = null
if has_empty_slot():
# ,
#
while slot == null: # ,
var temp_slot = inv.get_child(rng.randi_range(0, columns*rows-1))
if temp_slot.empty():
slot = temp_slot
break
return slot
func add_item(): # ,
var slot = get_empty_slot()
if slot:
var data = {"type":"", "count": 0}
data.type = "item_type_" + str(rng.randi_range(1, 8))
data.count = rng.randi_range(1, 999)
slot.update_data(data)
func find_slot(pos:Vector2, need_data = false): #
# - ,
for c in inv.get_children(): #
if (need_data and not c.empty()) or (not need_data):
if Rect2(c.rect_position, c.rect_size).has_point(pos):
# ,
#
return c
return null
func _process(delta):
var mouse_pos = get_viewport().get_mouse_position() #
if Input.get_mouse_button_mask() == BUTTON_LEFT: #
if not item_dragging: #
var slot = find_slot(mouse_pos, true)#
if slot: #
item_dragging = slot.item_data #
titem.set_data(item_dragging) #
titem.visible = true #
titem.rect_position = slot.rect_position #
prev_slot = slot #
slot.update_data() #
else: # , , ( )
titem.rect_position = lerp(titem.rect_position, mouse_pos - titem.rect_size/2, 0.3)
else: #
if item_dragging: #
var slot = find_slot(mouse_pos, false) #
if slot: # ,
if not slot.update_data(item_dragging): # ,
prev_slot.update_data(item_dragging)
prev_slot = null #
item_dragging = null #
titem.visible = false #
, :
? , )
func check_data(data):
return "all" in available_types or data.type in available_types
func update_data(data = null):
item.visible = data != null
item_data = data
if item_data:
if check_data(data):
item.set_data(item_data)
return true
return false
return true
, _process:
func _process(delta):
var mouse_pos = get_viewport().get_mouse_position() #
if Input.get_mouse_button_mask() == BUTTON_LEFT: #
if not item_dragging: #
var slot = find_slot(mouse_pos, true)#
if slot: #
item_dragging = slot.item_data #
titem.set_data(item_dragging) #
titem.visible = true #
titem.rect_position = slot.rect_position #
prev_slot = slot #
slot.update_data() #
else: # , , ( )
titem.rect_position = lerp(titem.rect_position, mouse_pos - titem.rect_size/2, 0.3)
else: #
if item_dragging: #
var slot = find_slot(mouse_pos) #
# â1
#if slot: # ,
#if slot.empty(): #
#if slot.check_data(item_dragging): # ,
#slot.update_data(item_dragging)
#else: # ,
#prev_slot.update_data(item_dragging)
#else: # , ,
#if slot.check_data(item_dragging) and prev_slot.check_data(slot.item_data):
#prev_slot.update_data(slot.item_data)
#slot.update_data(item_dragging)
#else: # ,
#prev_slot.update_data(item_dragging)
# â2
if slot: #
if slot.check_data(item_dragging): # ,
if slot.empty(): #
slot.update_data(item_dragging)
else: # ,
if prev_slot.check_data(slot.item_data): # ,
prev_slot.update_data(slot.item_data)
slot.update_data(item_dragging)
else:
prev_slot.update_data(item_dragging)
prev_slot = null #
item_dragging = null #
titem.visible = false #
, , , , , , , , , , , , , 100 , , , , , )
, , -, , - , , .
:
extends PanelContainer
signal dropped(data)
export (Array) var available_types = ["all"]
#
enum Actions {NONE, TRASH} #
var cur_act = Actions.NONE #
onready var item = $Item
var item_data = null #
func _ready():
update_data()
func set_action(new_value):
cur_act = new_value
$Item.visible = false
$Trash.visible = false
match cur_act:
Actions.NONE:
$Item.visible = true
Actions.TRASH:
$Trash.visible = true
func empty():
return item_data == null
func check_data(data):
if cur_act:
return true
return "all" in available_types or data.type in available_types
func update_data(data = null):
if data and cur_act:
emit_signal("dropped", data)
return true
item.visible = data != null
item_data = data
if item_data:
if check_data(data):
item.set_data(item_data)
return true
return false
return true
:
func ready():
titem.visible = false #
rng.randomize() #
$InvContainer/HBoxContainer/Clear.connect("pressed", self, "clear_inventory")
$InvContainer/HBoxContainer/Add.connect("pressed", self, "add_item")
inv.columns = columns # -
for i in range(columns*rows): #
var slot = slot_scene.instance() #
slot.name = "Slot%d" % i # , ,
slot.get_node("Num").text = str(i) # , ,
slot.set_action(slot.Actions.NONE)
if i == columns*rows-1:
slot.set_action(slot.Actions.TRASH)
slot.connect("dropped", self, "trash_dropped")
inv.add_child(slot) #
func trash_dropped(data):
print("dropped ", data)
_ready, , .
, .
:
Helmet , , .
:
extends PanelContainer
signal dropped(path, data) #
signal accepted(path, data) #
export (Array) var available_types = ["all"]
#
enum Actions {NONE, TRASH} #
var cur_act = Actions.NONE #
onready var item = $Item
var item_data = null #
func _ready():
set_action()
update_data()
func set_action(new_value = Actions.NONE):
cur_act = new_value
$Item.visible = false
$Trash.visible = false
$Num.visible = false
match cur_act:
Actions.NONE:
$Item.visible = true
$Num.visible = true
Actions.TRASH:
$Trash.visible = true
func empty():
return item_data == null
func check_data(data):
if cur_act:
return true
return "all" in available_types or data.type in available_types
func update_data(data = null):
if data and cur_act:
emit_signal("dropped", get_path(), data)
return true
item.visible = data != null
item_data = data
if item_data:
if check_data(data):
item.set_data(item_data)
emit_signal("accepted", get_path(), data)
return true
return false
return true
, :
extends Control
export (int, 1, 20) var columns = 8 #-
export (int, 1, 20) var rows = 4 #-
export (Array, NodePath) var slots_containers #
onready var slots = [] #
const slot_scene = preload("res://scenes/Slot.tscn") #
onready var inv = $PlayerInv/Inv/InvContent #
onready var titem = $TempItem # ,
onready var clearButton = $PlayerInv/Inv/Button/Clear
onready var addButton = $PlayerInv/Inv/Button/Add
onready var rng = RandomNumberGenerator.new() #
onready var item_dragging = null #
onready var prev_slot = null #
func ready():
titem.visible = false #
rng.randomize() #
clearButton.connect("pressed", self, "clear_inventory")
addButton.connect("pressed", self, "add_item")
inv.columns = columns # -
for i in range(columns*rows): #
var slot = slot_scene.instance() #
slot.name = "Slot%d" % i # , ,
slot.get_node("Num").text = str(i) # , ,
inv.add_child(slot) #
if i == columns*rows-1:
slot.set_action(slot.Actions.TRASH)
slots.push_back(slot)
for slots_node in slots_containers: #
for slot in get_node(slots_node).get_children():
slots.push_back(slot)
for slot in slots:
slot.connect("accepted", self, "slot_accepted")
slot.connect("dropped", self, "trash_dropped")
func slot_accepted(path, data):
print("accepted ", path, " ", data)
func trash_dropped(path, data):
print("dropped ", path, " ", data)
func clear_inventory(): #
for child in slots: #
child.update_data() #
func has_empty_slot(): #
for child in slots: #
if child.empty() and child.cur_act != child.Actions.TRASH:
return true
return false
func get_empty_slot(): #
var rand_slot = null
if has_empty_slot():
var empty_slots = [] #
for slot in slots: #
if slot.empty() and slot.cur_act != slot.Actions.TRASH:
empty_slots.push_back(slot)
rand_slot = empty_slots[(rng.randi_range(0, empty_slots.size()-1))] #
return rand_slot
func add_item(): # ,
var slot = get_empty_slot()
if slot:
var data = {"type":"", "count": 0}
data.type = "item_type_" + str(rng.randi_range(1, 8))
data.count = rng.randi_range(1, 999)
slot.update_data(data)
func find_slot(pos:Vector2, need_data = false): #
# - ,
for c in slots: #
if (need_data and not c.empty()) or (not need_data):
if c.get_global_rect().has_point(pos):
# ,
#
return c
return null
func _process(delta):
var mouse_pos = get_viewport().get_mouse_position() #
if Input.get_mouse_button_mask() == BUTTON_LEFT: #
if not item_dragging: #
var slot = find_slot(mouse_pos, true)#
if slot: #
item_dragging = slot.item_data #
titem.set_data(item_dragging) #
titem.visible = true #
titem.rect_position = slot.get_global_rect().position #
prev_slot = slot #
slot.update_data() #
else: # , , ( )
titem.rect_position = lerp(titem.rect_position, mouse_pos - titem.rect_size/2, 0.3)
else: #
if item_dragging: #
var slot = find_slot(mouse_pos) #
if slot: #
if slot.check_data(item_dragging): # ,
if slot.empty(): #
slot.update_data(item_dragging)
else: # ,
if prev_slot.check_data(slot.item_data): # ,
prev_slot.update_data(slot.item_data)
slot.update_data(item_dragging)
else:
prev_slot.update_data(item_dragging)
prev_slot = null #
item_dragging = null #
TatsĂ€chlich gibt es hier noch etwas zu verbessern. Es wĂ€re möglich, die Anzahl der Slots aufzugeben und alles ĂŒber das in Godot integrierte Tool zu erledigen, aber mehr dazu in einem der nĂ€chsten Artikel.
VollstÀndige Auflistung in meinem Github-Repository
UPD: Die Funktion get_empty_slot
in der letzten Liste wurde korrigiert , um die Möglichkeit zu beseitigen, in eine Endlosschleife zu geraten. Die Gita wird ebenfalls aktualisiert.
Auch in meinem Telegrammkanal können Sie die vorherigen Artikel lesen und als Erster Folgendes lesen - https://t.me/holydevlog