У меня есть game_board
сцена только с корнем Node2D
и без дочерних элементов.
Я хочу программно создавать игровые автоматы на этой плате.
Эти слоты должны быть созданы из отдельного файла сцены.
Поскольку в GDScript нет конструктора, я написал статический конструктор, инициализирующий необходимые члены (сейчас это просто slot_id: int
).
game_board.gd
extends Node2D
const play_slot_scene = preload("res://scenes/PlaySlot/play_slot.tscn")
func _ready():
if not play_slot_scene.can_instantiate():
push_error("Couldn't instantiate play slot")
var firstSlot: PlaySlot = play_slot_scene.instantiate()
firstSlot.slot_id = 0;
add_child(firstSlot)
for n in range(1,7):
var nextChild = firstSlot.constructor(n)
add_child(nextChild)
play_slot.gd
class_name PlaySlot
extends Node2D
const self_scene = preload("res://scenes/PlaySlot/play_slot.tscn")
@export var slot_id: int
static func constructor(id: int = 0)-> PlaySlot:
var obj = self_scene.instantiate()
obj.slot_id = id
return obj
Ошибка в коде var firstSlot: PlaySlot = play_slot_scene.instantiate()
, потому что firstSlot — это объект Node2d
(а не экземпляр класса PlaySlot
).
Если я удалю статическую типизацию, следующая строка завершится ошибкой, потому что slot_id
не существует в Node2D
.
Как мне создать экземпляры этих узлов с правильным классом? ТИА
@Theraot Это действительно к нему привязано.
В функции _ready()
на игровом поле экземпляры дочерних сцен не привязаны к прикрепленному сценарию.
По сути, экземпляр узла Node2D
и не может привязываться slot_id
Лучший способ добиться того, что я пытался, — это просто использовать static constructor()
. Это позволяет избежать предварительной загрузки PackedScene
.
game_board.gd
extends Node2D
@onready var play_slot_grid: GridContainer = %PlaySlotGrid
func _ready():
generate_play_slots()
func generate_play_slots():
for i in range(0,9):
var nextChild: PlaySlot = PlaySlot.constructor(i)
nextChild.name = "PlaySlot" + str(i)
nextChild.add_to_group("PlaySlots")
play_slot_grid.add_child(nextChild)
play_slot.gd
class_name PlaySlot
extends Control
# Nodes
const self_scene = preload("res://scenes/PlaySlot/play_slot.tscn")
# Members
@export var slot_id: int
static func constructor(id: int = 0)-> PlaySlot:
var obj = self_scene.instantiate()
obj.slot_id = id
return obj
PackedScene
Однако, если вам необходимо что-то делать с помощью PackedScene
, вам нужно иметь готовый экземпляр с instantiate()
.
game_board.gd
extends Node2D
@onready var play_slot = preload("res://scenes/PlaySlot/play_slot.tscn").instantiate()
func _ready():
# Doesn't complain because play_slot is not Node2D here
play_slot.slot_id = 123
Я полностью озадачен этой проблемой, и единственное решение, которое я нашел, — это вместо жесткого кодирования определения play_slot_scene
для вызова preload
определить его как переменную @export
и назначить сцену через редактор. Это... каким-то образом... заставляет вызов instantiate
на сцене фактически создавать узел, корень которого выполняет на нем скрипт, тогда как использование preload
почему-то этого не делает. Мне очень понравился этот движок в версии 3.x, но до сих пор мой опыт был весьма разочаровывающим из-за подобных проблем.
Я думаю, что это та же самая проблема, о которой я тоже спотыкаюсь. Я хочу создать строго типизированный узел сцены, который можно будет использовать как динамически в коде, так и прикрепить к другой сцене (если пользователю просто нужен один экземпляр на всю жизнь своей сцены). Такое ощущение, что GDScript объединяет «как создать экземпляр узла» и «тип узла», что препятствует хорошему решению. Есть ли у вас «решение» на случай прикрепления узла как сцены, которое было бы менее запутанным, чем использование @onready
выше?
@Дэвид - «Правильный путь» не использует @onready для PlaySlots
. Я использовал его для создания узла контейнера. Если вы прикрепите его к self
, он будет работать без предварительной загрузки/готовности.
То, что вы описываете, говорит о том, что скрипт
play_slot.gd
не привязан к корнюres://scenes/PlaySlot/play_slot.tscn
сцены.