Assigning variables to instanced objects?

Godot Version

4.2.2

Question

I have a Node2D, a Shooter node that spawns projectiles based on a timer. Basically, it goeth

@export var time_between_shots:float
@export var projectile:PackedScene
@export var projectile_disappear_time:float

@onready var spawn_point:Marker2D = $SpawnPoint
@onready var shoot_timer:Timer = $ShootTimer
@onready var projectile_velocity:Vector2 = spawn_point.global_position - global_position

func _ready():
	shoot_timer.wait_time = time_between_shots
	shoot_timer.timeout.connect(shoot)
	shoot_timer.start()

func shoot():
	var inst:Projectile = projectile.instantiate()
	owner.add_child(inst)
	inst.transform = spawn_point.global_transform
	inst.proj_velocity = projectile_velocity
	inst.disappear_time = projectile_disappear_time

It spawns projectiles, basically, and sets their speed to a speed based off of another node… Here is the projectile code:

extends Area2D

class_name Projectile
var proj_velocity:Vector2
var disappear_time:float
@onready var bye_timer:Timer = $ByeTimer

func _ready():
	bye_timer.wait_time = disappear_time
	bye_timer.timeout.connect(func(): queue_free())
	bye_timer.start()

func _physics_process(delta:float) -> void:
	position += proj_velocity

Here’s the problem: The wait_time isn’t set correctly, which is extra weird because the proj_velocity variable gets set with ease.

I checked, and upon using print(inst.disappear_time) on the Shooter node, it gives the correct time (say, 2.5 seconds), but doing the same print(disappear_time) reveals that it was set to 1.0 automatically. This has had me stumped for a day.

1 Like

Try this

func shoot():
	var inst:Projectile = projectile.instantiate()
	inst.transform = spawn_point.global_transform
	inst.proj_velocity = projectile_velocity
	inst.disappear_time = projectile_disappear_time
    owner.add_child(inst)
2 Likes

owner is an odd pick here, it might lead to unexpected parents, try add_sibling or setting an explicit node through an @export var project_parent: Node value

@syntaxerror247 is right that you need to call add_child later, _ready() is called at the same time as add_child.

1 Like

If owner is likely to lead to unexpected parents, what less risky uses are there for it? In the case of the game I’m making, I’m only ever expecting the owner to be the Current Scene, or maybe a Node2D to contain all Shooters

Seems like you have a firm grasp on owner, I’d still call it an odd pick, but I see some getting tricked by it thinking it’s the same as get_parent(). The less risky option would be add_sibling or picking a node through @export

1 Like

Got it. Also, you seem knowledgeable, I might as well ask:

Why didn’t inst.proj_velocity = projectile_velocity cause the same issue? Is it because it’s a Class, a Vector2D, instead of a basic type, and those do things differently?

The problem is only the order of events. I’ll put your original sample in-order

### starting in: func shoot():
var inst:Projectile = projectile.instantiate()
owner.add_child(inst)
### `add_child` calls func _ready():

# disappear_time has not been set yet!
bye_timer.wait_time = disappear_time 
bye_timer.timeout.connect(func(): queue_free())
bye_timer.start()

### _ready finishs, back to finish up in func _shoot():
inst.transform = spawn_point.global_transform
inst.proj_velocity = projectile_velocity
# finally disappear_time is set, but it's far too late!
inst.disappear_time = projectile_disappear_time

Since proj_velocity is applied every physics process it does not matter when the value is set relative to _ready(), but the timer does care, becase it’s wait_time is only set once on _ready().

Another small nit-pick is you can connect to queue_free directly instead of through a lambda

bye_timer.timeout.connect(queue_free) # no parenthesis
1 Like

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