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:
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.
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)
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)
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
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)
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
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!)