Posts
Search
Contact
Cookies
About
RSS

Godot scenes as components - the importance of local to scene.

Added 17 Feb 2025, 6:53 a.m. edited 17 Feb 2025, 7:55 a.m.

You probably won't need to have used Godot for very long before you notice that if you duplicate a Node with a child of (for example) a mesh instance, then edit the Albedo of the mesh instance, that both nodes change colour.

This can be less than intuitive for the beginner - and even for a seasoned C hack of decades experience it can seem a little odd.  Its worth just briefly touching on just what is going on here...  When Godot duplicates something (or instantiates a scene) rather than make copies of everything, certain resources become references (think pointers in C) this means there is only ever one copy in memory of a particular resource (which could be quite large) - great for efficiency, but it can lead to some frustrating behaviour if you're not sure what is going on.

The trick to this, is to notice that any resource of a node will have a "local to scene" option, but more about this later...

We're going to look at a concrete example, in this case we want to end up with a reusable laser beam component, that can be switched on and off, and also have a property to define the beam length.  In order to do this we'll have a couple of properties that can be changed within the Godot IDE.

@tool
extends Node3D

@onready var beam: MeshInstance3D = $body/beam
@onready var area_3d: Area3D = $body/beam/Area3D
@onready var area_shape: CollisionShape3D = $body/beam/Area3D/areaShape

@export var beam_length: float :
set(value):
beam_length = value
if beam:
update_beam()

@export var is_active: bool :
set(value):
is_active = value
if beam:
if is_active:
beam.visible = true
else:
beam.visible = false

We've made this a tool script so we can get visual feedback from our edits, as you can see when the IDE changes the beam length we call a function, but more on that later, for now notice that anywhere that "beam" (the mesh instance) is referenced it is behind a "null guard" ( if beam: ) this is important as the IDE will change these properties before everything is ready and for a short time beam will indeed be null !

func _ready() -> void:
beam_length = beam_length

func update_beam():
if beam:
beam.mesh.height = beam_length
beam.transform.origin.y = beam_length / 2.0 + 0.25
var mat:ShaderMaterial = beam.get_active_material(0)
mat.set_shader_parameter("beam_length", beam_length)
area_shape.shape.height = beam_length

Setting beam_length to itself, may seem counter intuitive, but just ensures that everything does actually get set up after "beam" is valid (ready) but likely a simple call to update beam is all that is required.

At this point we now know what we need to pay attention to, in regards to making stuff "local to scene".  With the exception of the transform everything that is getting changed here needs to be local to the scene.

The trick here is to get on the node property window and filter it with "local to" expanding out the tree to find all the resources we need, the beam mesh, the shader material and the shape of the area3d all need to be set as "local to scene"

Now in a scene where you want to use the lasers, instance the scene a number of times, it's now possible to change the length of beams with out effecting other ones, and switch them on and off independently.  You might want to experiment initially with a simpler example of a scene with a mesh instance and a property that changes the meshes albedo.

Wherever you can, try to avoid making these instances local with "make local" (in the scene hierarchy right click menu) as this will plop the whole hierarchy of the child scene in your scene and make it truly independent.  Instead have a single node with independent properties, and if you later decide that you want all your lasers to behave differently, if they are still just instanced, then changes to the original laser scene will effect every child in you whole project - which can be great if you have loads of them, meanwhile they all preserve their own independent properties like active and length.

I think this is probably one of the biggest point of confusion for people new to Godot, and one that seems to get almost no coverage! 

Making customisable, reusable child scenes that can be used throughout your project is a powerful and convenient work flow, that will greatly enhance your projects.

Enjoy!