Quick answer: Move node access to _ready(). Use _init() only for non-tree state. Pass dependencies via @export or constructor args.
You write a constructor that accesses $Sprite. Crashes with null. The sprite isn’t in the tree yet at _init time.
The Fix
extends Node2D
@onready var sprite: Sprite2D = $Sprite
@onready var tween_target: Vector2 = sprite.position
func _init():
# sprite is null here — node tree not assembled
# Use _init only for non-node setup
pass
func _ready():
# sprite is valid here
sprite.modulate = Color.WHITE
sprite.position = tween_target
The lifecycle: instantiate → _init → add to tree → _enter_tree → children added → _ready. Resolve children in _ready.
Verifying
Place sprite access in _ready: works. Same line in _init: null reference error. Confirms timing.
“_init for data. _ready for children. @onready waits.”
Related Issues
For C# signal emit, see signal emit. For Resource UID, see UID.
_ready is when. _init too soon.