Как я могу динамически создавать экземпляры узлов из сцены с помощью конструктора?

У меня есть 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.

Как мне создать экземпляры этих узлов с правильным классом? ТИА

То, что вы описываете, говорит о том, что скрипт play_slot.gd не привязан к корню res://scenes/PlaySlot/play_slot.tscn сцены.

Theraot 22.04.2024 15:00

@Theraot Это действительно к нему привязано.

Krishnabm 24.04.2024 15:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
1 328
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В функции _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, но до сих пор мой опыт был весьма разочаровывающим из-за подобных проблем.

Atul Varma 25.05.2024 23:04

Я думаю, что это та же самая проблема, о которой я тоже спотыкаюсь. Я хочу создать строго типизированный узел сцены, который можно будет использовать как динамически в коде, так и прикрепить к другой сцене (если пользователю просто нужен один экземпляр на всю жизнь своей сцены). Такое ощущение, что GDScript объединяет «как создать экземпляр узла» и «тип узла», что препятствует хорошему решению. Есть ли у вас «решение» на случай прикрепления узла как сцены, которое было бы менее запутанным, чем использование @onready выше?

David 07.06.2024 00:17

@Дэвид - «Правильный путь» не использует @onready для PlaySlots. Я использовал его для создания узла контейнера. Если вы прикрепите его к self, он будет работать без предварительной загрузки/готовности.

Krishnabm 24.06.2024 19:37

Другие вопросы по теме