Skip to content

SingletonNode

SingletonNode is a helper base class that guarantees a single active instance per script. If another instance of the same script enters the scene tree, the duplicate is freed automatically.

It covers two usage patterns:

  • Scene-based — the node lives as a child of a scene and is destroyed when that scene exits.
  • Manual root — the node is created from code, mounted on root, and persists across scene changes.
  1. Download ss-gameforge-singleton from the releases page.
  2. Copy addons/ss-gameforge-singleton/ into your project’s res://addons/.
  3. Enable ss-gameforge-singleton in Project Settings → Plugins.
MethodDescription
SingletonNode.get_instance_for(script)Returns the active instance for a given script class.
SingletonNode.ensure_for(script, root, name)Creates and adds the instance if it doesn’t exist yet.
is_active_instance()Returns true if this node is the current active singleton.

Use this when the singleton only makes sense inside a specific scene and should be destroyed when leaving it: a boss fight director, a puzzle controller, a run session manager.

Extend SingletonNode and expose a typed static accessor:

extends SingletonNode
class_name BossFightDirector
static var i: BossFightDirector:
get:
return SingletonNode.get_instance_for(BossFightDirector) as BossFightDirector
var phase := 1
func next_phase() -> int:
phase += 1
return phase

Add the node as a child of the boss fight scene. If a duplicate enters the tree it is freed automatically.

# safe from anywhere — returns null if not in the boss scene
if BossFightDirector.i:
BossFightDirector.i.next_phase()

Use this when the singleton must persist across scene changes but you want to control exactly when it is created — a save service created only after session start, a network service only when connected.

extends SingletonNode
class_name SaveService
static var i: SaveService:
get:
return SingletonNode.get_instance_for(SaveService) as SaveService
func save_game() -> void:
print("[SaveService] Saving...")

Create it from code and mount it on root:

func _ready() -> void:
SingletonNode.ensure_for(SaveService, get_tree().root, "SaveService")

The node now lives under root, survives change_scene_to_*, and is accessible via SaveService.i from anywhere.