@export and underscore members in Godot 4: how to keep internal GDScript state from leaking into scenes
0 reputation · 31 Jul 2021, 09:11 UTC
0 reputation · 31 Jul 2021, 09:11 UTC
I'm designing a small Godot 4.x project where several scripts hold internal state that should stay private to their owning node. My understanding is that GDScript has no enforced access modifiers, so the leading-underscore convention is the only signal of intent, and any script that can reach the node can still read or write those members at runtime.
The part that worries me most is @export. Exported variables show up in the Inspector and get serialized into .tscn and .tres files, which means values can end up committed to version control or tweaked by designers without touching code. I want the convenience of Inspector editing for genuinely tunable values without accidentally exposing state that should never be persisted or mutated from outside.
Assume Godot 4.x with GDScript; I can verify behavior in a fresh project by saving scenes and inspecting the serialized files.
Is there any supported mechanism beyond naming convention to restrict access to a script's members from other nodes? When a property must be Inspector-visible, is there a way to keep it out of the serialized scene file? And given that enforced private/protected modifiers remain unimplemented, what is the recommended structure for keeping internal state from becoming de facto public API?
35275 reputation · 31 Jul 2021, 16:13 UTC
Godot 4.x does not provide any language-level access control for GDScript members. The leading-underscore convention is the only signal of intent, and any script that obtains a reference to the node can read or write those members at runtime. Moreover, every variable marked with @export is always serialized into the scene (.tscn) or resource (.tres) file; there is no attribute to keep an exported variable visible in the Inspector while omitting it from serialization.
private, protected, or public keywords.@export var speed to a script and save the node, the generated .tscn will contain a line like speed = 10. An underscore-prefixed variable such as _speed never appears in the file.get_node("...")._speed = 5 works fine, demonstrating that the underscore convention does not enforce privacy._value.@export variable that forwards to the private storage via a setter/getter.# MyNode.gd
extends Node
# ---- internal state (private by convention) ----
var _value: float = 0.0 # not exported, not serialized
var _timer: float = 0.0 # internal only
# ---- inspector-editable proxy ----
@export var value: float:
get: return _value
set(v):
_value = v
# optional: react to the change here
_on_value_changed()
func _on_value_changed() -> void:
# react when the inspector changes the value
pass
func _process(delta: float) -> void:
_timer += delta
# use _value and _timer internally; they are never visible in the Inspector
Because only the exported proxy (value) appears in the saved scene, the internal _value and _timer stay out of the file and are not accidentally committed to version control. Other scripts can still access _value if they obtain a reference, but the naming convention makes the intent clear and can be enforced via code review.
There is no built-in way to have an exported variable that is editable in the Inspector and omitted from the scene file. If persistence is unacceptable, avoid @export for that value and instead:
@tool) that modifies a private variable at runtime, orAccepting the exported variable's presence in the file is the simplest and most reliable approach for most tunable parameters.
Enforced privacy does not exist in GDScript; rely on underscore naming, code review, and the pattern of exporting a proxy that forwards to a private backing variable. Exported variables always serialize, so keep truly internal state out of @export and accept that any exported value will be persisted.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.