2.1 KiB
2.1 KiB
| description | globs | alwaysApply |
|---|---|---|
| Godot client — keep main.gd thin; split scripts by concern (Neon Sprawl). | client/**/*.gd | false |
Godot client script organization (Neon Sprawl)
Avoid a single monolithic main.gd. Treat the main scene root as a composer: wire nodes, @export references, and signals—not every subsystem.
Where logic lives
main.gd(scene root): Bootstrapping only—attach children, connect signals, optional high-level toggles. If a block grows past ~50 lines or a clear concern appears, extract it.- Avatar / props: Keep behavior on the node that owns it (e.g.
player.gdonCharacterBody3D). - Cross-cutting features: Prefer a child
Node(orNode3D) with its own script, e.g. ground picking →ground_pick.gd, server HTTP sync →position_authority_client.gd. Use signals or small public methods somaindoes not call into deep implementation details.
class_name vs headless / CI
class_name registers global types only after Godot has imported the project (.godot/). That folder is gitignored here, so godot --headless --path client on a fresh clone fails if main.gd annotates types with class_name aliases. Prefer Node / Node3D + connect("signal", …) and call("method", …) for thin composition from the scene root unless the project guarantees a prior editor import.
Autoloads
Use sparingly (e.g. shared config many scenes need). Default to scene-local nodes until duplication proves an autoload.
Granularity
- One obvious concern per script (picking vs network vs locomotion)—not one file per tiny helper.
- New work: add
res://scripts/<feature>.gd(or a subfolder when a feature set grows) rather than extendingmain.gdby default.
Examples
✅ main.gd: _ready() → get_node, connect ground_pick.target_selected to _on_target
✅ ground_pick.gd: raycast + walkable check → emit target_selected(Vector3)
❌ main.gd: 300 lines of HTTP + ray math + player tuning in one file
Aligns with GDScript style (small virtual overrides, extract _ helpers).