Issue with instantiating scene

Godot Version

4.4.1

Question

I’m trying to instantiate a scene into my game.

The instantiate code runs when the main scene starts, which it seems to do fine because it doesn’t give any error messages, but nothing spawns in. I thought it was just spawning it somewhere out of sight, but after setting up some prints in the spawnable scenes code, it looks like nothing is being instantiated at all.

Here is the code I’m using to instantiate the scenes, along with their references:

This is part of the custom-class script attached to the spawnable scenes (neither the print or print_debug is showing up in the output)

I’m assuming there’s something wrong with the references, since I previously had an error message that was cleared up once I changed the instantiate code, but I can’t seem to figure out what’s wrong. Any help is appreciated!

In the future please copy and paste your code and place it between ``` on the lines above and below.

Don’t use @onready variables. Use constants.

const BASS = preload("res://scenes/bass.tscn")

var new_bass = BASS.instantiate()

Also, be aware of naming conventions in the GDScript Style Guide. The recommendations are there for many reasons. One important, but often overlooked one (until your export fails to work correctly on windows) is that having uppercase letters in Windows paths or filenames will cause your export problems.

1 Like

Switched to constants, but nothing seems to have changed.
Here is the script for the spawn control node, I can post the the custom class script too, its just a little big and messy (sorry for not posting it before, didn’t know how to lol)

extends Node3D

@onready var spawn_radius_shallows: Area3D = $SpawnRadiusShallows
@onready var spawn_radius_deep: Area3D = $SpawnRadiusDeep
@onready var spawn_radius_abyss: Area3D = $SpawnRadiusAbyss
@onready var proto_controller: CharacterBody3D = $"../ProtoController"

const BASS = preload("res://Scenes/bass.tscn")
const SQUID = preload("res://Scenes/squid.tscn")
const BARRACUDA = preload("res://Scenes/barracuda.tscn")
const ANGLER = preload("res://Scenes/angler.tscn")

#var shallow_fish = [bass, squid]
#var deep_fish = [barracuda]
#var abyss_fish = [angler]

var shallow_count = 0
var deep_count = 0
var abyss_count = 0

func _ready() -> void:
	spawn_shallows()

func spawn_shallows():
	var next_shallows = randi_range(1, 2)
	if next_shallows == 1:
		var new_bass = BASS.instantiate()
		get_tree().current_scene.add_child(new_bass)
		new_bass.global_position = proto_controller.global_position
		print_debug("BASS")
	elif next_shallows == 2:
		var new_squid = SQUID.instantiate()
		get_tree().current_scene.add_child(new_squid)
		new_squid.global_position = proto_controller.global_position
		print_debug("SQUID")

func spawn_deep():
	pass

func spawn_abyss():
	pass

Also thanks for the advice on naming conventions, I’ll be sure to check that out.

np that’s why I told you how.

Specifically, rename your Scenes folder to scenes.

Yeah that probably wasn’t your only issue then. An @onready variable should work depending on timing. My suggestion was also for a number of other reasons. It saves time when executing the code, they get loaded first so you don’t have race conditions, it’s the appropriate convention…

My next guess is it’s your randomization code. I recommend you just take that out and get one fish working before you try randomizing it. Also, when you do, you already have an array shallow_fish So I’d do this:

extends Node3D

#Constants go at the top of the file.
const BASS = preload("res://Scenes/bass.tscn")
const SQUID = preload("res://Scenes/squid.tscn")
const BARRACUDA = preload("res://Scenes/barracuda.tscn")
const ANGLER = preload("res://Scenes/angler.tscn")

#Variables next
var shallow_fish = [BASS, SQUID]
var deep_fish = [BARRACUDA]
var abyss_fish = [ANGLER]

var shallow_count = 0
var deep_count = 0
var abyss_count = 0

#@Onready variables after that.
@onready var spawn_radius_shallows: Area3D = $SpawnRadiusShallows
@onready var spawn_radius_deep: Area3D = $SpawnRadiusDeep
@onready var spawn_radius_abyss: Area3D = $SpawnRadiusAbyss
@onready var proto_controller: CharacterBody3D = $"../ProtoController"


func _ready() -> void:
	spawn_shallows()


func spawn_shallows():
	var new_fish = shallow_fish.pick_random().instantiate()
	get_tree().current_scene.add_child(new_fish)
	new_fish.global_position = proto_controller.global_position
	print_debug(new_fish.name)

See how that works for you.

1 Like

Doesn’t look like it worked, the code looks a lot more organized though lol.

What’s strange is that this only happens when I try to parent the fish on the main scene. Before, they were being parented to a Node3D, which works in spawning them, but runs into an error connecting the custom class to one of it’s references. I’m not sure if this problem would still happen being parented to the main scene. Since the instantiation isn’t working, the fish’s custom class script doesn’t run.

It makes me think there’s a problem with the scene tree, but I can’t find anything wrong with it.

I’m gonna post the custom class script, just in case I didn’t explain that last part right. Again, apologies for the messy code :folded_hands:

extends Node3D
class_name  Fish

@export var health = 10
@export var speed = 5
@export var action_speed = 10
#MOVE DURATION MUST BE LOWER THAN TIMER
@export var move_duration = 5
@export var damage_level = 1
@export var animated_sprite : AnimatedSprite3D
@export var move_timer : Timer
@export var dead_timer : Timer
@export var idle_timer : Timer
@export var return_timer : Timer
@export var flee_area : Area3D
@export var defend_area : Area3D
@export var attack_area : Area3D
@onready var spawn_radius_shallows: Area3D = $"../FishSpawnControl/SpawnRadiusShallows"
@onready var return_point_shallows: Node3D = $"../FishSpawnControl/SpawnRadiusShallows/ReturnPointShallows"
@onready var spawn_radius_deep: Area3D = $"../FishSpawnControl/SpawnRadiusDeep"
@onready var return_point_deep: Node3D = $"../FishSpawnControl/SpawnRadiusDeep/ReturnPointDeep"
@onready var spawn_radius_abyss: Area3D = $"../FishSpawnControl/SpawnRadiusAbyss"
@onready var return_point_abyss: Node3D = $"../FishSpawnControl/SpawnRadiusAbyss/ReturnPointAbyss"

enum ZONES {shallows,deep,abyss}
@export var Zone : ZONES

@export var is_grabable : bool
@onready var proto_controller: CharacterBody3D = $"../ProtoController"

var is_dead : bool = false
var state = IDLE
enum {IDLE, FLEE, DEFEND, ATTACK, RETURN}
var current_pos : Vector3
var new_pos : Vector3
var tween : Tween
var direction
signal damage(value)

func _ready() -> void:
	print("WORKING")
	move_timer.start()

	if flee_area:
		flee_area.body_entered.connect(_on_body_entered)
		flee_area.body_exited.connect(_on_body_exited)
		print_debug("CONNECT FLEE")
	elif !flee_area:
		null
	if defend_area:
		defend_area.body_entered.connect(_on_body_entered)
		defend_area.body_exited.connect(_on_body_exited)
		print_debug("CONNECT DEFEND")
	elif !defend_area:
		null
	if attack_area:
		attack_area.body_entered.connect(_on_body_entered)
		attack_area.body_exited.connect(_on_body_exited)
		print_debug("CONNECT ATTACK")
	elif !attack_area:
		null

	if Zone == ZONES.shallows:
		spawn_radius_shallows.area_exited.connect(_shallows_exited)
		print_debug("CONNECT SHALLOWS")
	elif Zone == ZONES.deep:
		spawn_radius_deep.area_exited.connect(_deep_exited)
		print_debug("CONNECT DEEP")
	elif Zone == ZONES.abyss:
		spawn_radius_abyss.area_exited.connect(_abyss_exited)
		print_debug("CONNECT ABYSS")

func _process(delta: float) -> void:
	match state:
		IDLE:
			animated_sprite.animation = "default"
		FLEE:
			direction = (proto_controller.global_transform.origin - self.global_transform.origin).normalized()
			position += -direction * action_speed * delta
			animated_sprite.animation = "Action"
		DEFEND:
			direction = (proto_controller.global_transform.origin - self.global_transform.origin).normalized()
			position += direction * action_speed * delta
			animated_sprite.animation = "Action"
		ATTACK:
			direction = (proto_controller.global_transform.origin - self.global_transform.origin).normalized()
			position += direction * action_speed * delta
			animated_sprite.animation = "Action"
		RETURN:
			if Zone == ZONES.shallows:
				direction = (spawn_radius_shallows.global_transform.origin - self.global_transform.origin).normalized()
			elif Zone == ZONES.deep:
				direction = (spawn_radius_deep.global_transform.origin - self.global_transform.origin).normalized()
			elif Zone == ZONES.abyss:
				direction = (spawn_radius_abyss.global_transform.origin - self.global_transform.origin).normalized()
			position += direction * action_speed * delta
			animated_sprite.animation = "Return"
	
func _on_timer_timeout() -> void:
	if state == IDLE && !is_dead:
		current_pos = self.global_position
		new_pos = self.global_position
		var target_pos = Vector3(randf_range(-100,100), randf_range(-100,100), randf_range(-100,100))
		new_pos = current_pos + target_pos
		tween = get_tree().create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE)
		tween.tween_property(self, "position", new_pos, move_duration)

func _on_body_entered(body: CharacterBody3D) -> void:
	if state != RETURN:
		tween.kill()
		move_timer.stop()
		idle_timer.stop()
		if flee_area && !is_dead:
			state = FLEE
			print_debug("FLEE")
		if defend_area && !is_dead:
			state = DEFEND
			print_debug("DEFEND")
		if attack_area && !is_dead:
			state = ATTACK
			print_debug("ATTACK")

func _on_body_exited(body: CharacterBody3D) -> void:
	if state != RETURN:
		print_debug("IDLE")
		idle_timer.start()


func _on_damage_area_body_entered(body: Node3D) -> void:
	emit_signal("damage", damage_level)
	print_debug("DAMEGG")  

func take_damage():
	if health <= 0:
		is_dead = true
		is_grabable = true
		dead_timer.start()

func idle_timeout():
	if state != RETURN:
		idle_timer.stop()
		move_timer.start()
		state = IDLE

func _shallows_exited(body: Node3D) -> void:
	print_debug("SHALLOWS")
	state = RETURN
	return_timer.start()

func _deep_exited(body: Node3D) -> void:
	print_debug("DEEP")
	state = RETURN
	return_timer.start()

func _abyss_exited(body: Node3D) -> void:
	print_debug("ABYSS")
	state = RETURN
	return_timer.start()

func return_timeout():
	return_timer.stop()
	state = IDLE

func dead_timer_timeout():
	queue_free()

(The spawn radius signals in the ready function is what was giving the error before)

Do you get an error like this? :

Parent node is busy setting up children, add_child() failed. Consider using add_child.call_deferred(child)` instead.

I think you get this error but you didn’t notice.
You are trying to add a child to a parent node while it is trying to initialize its children. Some options you can do:

  • You can add it to a child node of the fish_spawn_control insteaf of get_tree.current_scene
  • You can move spawn_shallows() call somewhere that it won’t be called in _ready function if it’s really important that you have to add it to as a child of get_tree.current_scene.
  • You can use call_deferred like suggested on the error
  • You can wait for 1 frame, then call spawn_shallows function. This is the code for delaying the execution one frame: await get_tree().process_frame
1 Like

Yep, completely missed that error message. Moved it out of the ready function and it seems to be working fine for now. Thank you so much! (and thank you to Dragonforge as well!)

2 Likes

You can also do this:

func _ready() -> void:
	ready.connect(_on_ready)


func _on_ready() -> void:
	spawn_shallows()

This waits until the node is completely setup by waiting for the ready signal. Then it adds new things.

2 Likes

Awesome, I’ll probably end up doing it that way. Thanks!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.