From aaf8b400efcb9ca06d3437fe7ab7c74b04c33926 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 8 Sep 2026 22:35:41 +0200 Subject: [PATCH] Build the accretion disc once per spawn, not twice Planet.gd calls visual.configure() BEFORE add_child(), and the SceneTree defers _ready() to the first frame - so configure() built the five rings, and _ready() then threw them away and built five more. Eighty antialiased polylines tessellated per black hole instead of forty, plus five nodes queued for free. Confirmed by instrumenting _build(): _build() called, existing children=0 <- configure() _build() called, existing children=5 <- _ready() _ready() now uses the same is-it-built guard configure() already had. Note for anyone chasing the same thing: this is NOT the disc's steady-state cost. Measured, the geometry rasterises zero times over sixty frames (the NeonPiece refactor works), and rendering the disc costs 0.1 ms against a 133 ms softpipe floor. The disc is cheap once it exists. 172/172. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD --- planets/BlackHoleVisual.gd | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/planets/BlackHoleVisual.gd b/planets/BlackHoleVisual.gd index ab0cb0e..ea664ac 100644 --- a/planets/BlackHoleVisual.gd +++ b/planets/BlackHoleVisual.gd @@ -26,7 +26,12 @@ var _rings: Array = [] func _ready() -> void: - _build() + # Planet.gd calls configure() BEFORE add_child(), so the disc is usually + # already built by the time _ready fires (which the SceneTree defers to + # the first frame). Without this guard the ring set was thrown away and + # re-tessellated - eighty antialiased polylines per spawn instead of forty. + if _rings.is_empty(): + _build() func configure(_shape_seed: int) -> void: