Krishnabm
Krishnabm

Reputation: 185

How can I dynamically instantiate nodes from a scene using a constructor?

I have a game_board scene with only a root Node2D and no children. I wish to programmatically create playing slots on this board. These slots are to be instantiated from a separate scene file.

Since GDScript doesn't have a constructor, I wrote a static constructor initializing the required members (currently just a 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

The code errors out at var firstSlot: PlaySlot = play_slot_scene.instantiate() because firstSlot is a Node2d obj (not an instance of PlaySlot class).

If I remove the static typing, the next line fails because slot_id does not exist on Node2D.

How do I instantiate these nodes with the right class? TIA

Upvotes: 0

Views: 5792

Answers (1)

Krishnabm
Krishnabm

Reputation: 185

At the _ready() function in the game board, instanced child scenes have not bound to the attached script. In essence, the instanced node is Node2D and can't bind slot_id

The right way

A better way do achieve what I was trying is to simply use the static constructor(). This avoids preloading the 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

Using a PackedScene

However, if you must do things using a PackedScene, you need to have an instance ready with 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

Upvotes: 1

Related Questions