Godot Version
v4.1.1.stable
Question
I’m trying to create a spawn area for my asteroids game, and part of what I’m trying to do is have the asteroid scene set up like this:
RigidBody2D (script)
–Sprite2D
–CollisionBody2D
The spawner’s tree looks like this:
Node2D (script)
–Timer
And the key point is that the sprite2Ds texture is a spritesheet with all the variations of asteroid for that scene in particular. I’m trying to have the script on the asteroid pick one of the frames at random, and set that as the frame for the instanced asteroid. The code looks like this:
extends RigidBody2D
@onready var sprite = $Sprite2D
func _ready() -> void:
sprite.set_frame([0,1,2].pick_random()) # 0,1 and 2 are the frames the sheet has
As for the spawner, it tries to spawn an asteroid every 4 seconds, but when it does that, I get an error in the asteroid’s code:
“Attempt to call function ‘set_frame’ in base ‘null instance’ on a null instance.”
I understand that this means the Sprite2D isnt ready when the set_frame() function is called. In fact, in the debugger the variable “sprite” shows up as null.
The curious thing is that this only happens when I try to spawn the asteroid through the spawner’s code. When I simply put a few copied of the scene in the editor and run the game, they randomize just fine.
I guess it depends on the order of _ready
functions calls and the variable assignments using @onready
. I don’t know the details, and I might be wrong, but I believe the only solution here is to get the sprite in the ready function:
var sprite: Sprite2D = null
func _ready() -> void:
sprite = $Sprite2D
sprite.set_frame([0,1,2].pick_random())
Should that not be AnimatedSprite2D
?
What I’ve found easiest in Godot is to create the sprite in code:
extends RigidBody2D
var sprite: AnimatedSprite2D
func _ready() -> void:
sprite = AnimatedSprite2D.new()
sprite.sprite_frames = preload("res://asteroid_sprite_frames")
sprite.frame = [0, 1, 2].pick_random()
sprite.speed_scale = 0.0
add_child(sprite)
As far as I understand, that’s what @onready
is syntactic sugar for, so I don’t think it makes a difference. I could be wrong, though…
1 Like
Are you spawning the asteroids with the .gd file? or the .tscn file
Sprite2D has a set_frame
function to change the displayed sprite on the sheet, it doesn’t have to be animated for that.
Also, while creating the sprite dynamically on ready would work, it prevents the sprite edition in the editor (which is way easier for positioning, scaling, color, etc.).
I agree, but depending on the order, it could lead to issues. For instance, if the _ready
function is called before the @onready
variable, then the variable is not set and there’s an error.
I use C# in Godot so I’m not used to @onready
at all, but I don’t really like the idea of having two things happening at the same time, at different places in the code, so… I wouldn’t be using @onready
too much anyway. 
@onready
variables are all set before the _ready
function is called
I’m spawning it with the .gd file. Is that the problem?
Yep, that was the problem. I was using the .gd file for the preload statement, instead of .tscn
1 Like