Animate via scene nodes instead of per-frame _draw redraws #10
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to #6. That was fixed by splitting static art onto a draw-once layer and capping animated redraws at 30Hz. Capping is a mitigation, not a cure - the frames it does draw still re-tessellate every polyline.
The idea
Stop redrawing, and let the renderer's cached geometry do the work: decompose animated art into child CanvasItems drawn ONCE, then animate their
transform/modulate/visiblewith Tween or AnimationPlayer. Changing those does not re-run_draw.One important nuance
"Use AnimationPlayer" on its own does not help. If
_drawstill derives its geometry from a time variable, animating that variable with a Tween still needs aqueue_redrawto show anything - same cost, more machinery. The saving comes from the decomposition into nodes, with the animation framework as the natural driver.This is already the house pattern
It is proven in this codebase, not speculative:
Ship.gdcallsqueue_redraw()exactly once, in_ready, and rotates purely via therotationproperty. Ships visibly turn with zero redraws.Asteroid.gdsays so explicitly: "Deliberately no queue_redraw() here. position and rotation are node properties".So the work is bringing the stragglers in line, not inventing an approach.
What still redraws every frame
Building.gd- the crafter's assembly line and the mothership's core/satellites (now capped to 30Hz, still per-frame within that)Projectile.gd-_drawderives fromageExplosionVFXBase.gd- all the explosion VFXSketch of the conversion
rotationis animated. Orbit becomes one transform update.scale+modulate.ainstead of recomputing a hexagon each frame.position.xalong the rail.visible(andmodulatefor the attach flare) instead of redrawing the set.position.yandmodulate.a.scale/modulateover its life.Where it genuinely does not fit
The weld sparks jitter randomly every frame - that is procedural, not a tween. Either keep a tiny redraw for that one element (a handful of short lines, not a whole yard) or express it as a few child nodes with animated rotation. Worth deciding during the work rather than forcing it.
Definition of done
Building.gd,Projectile.gd,ExplosionVFXBase.gdno longer callqueue_redrawfrom_processtest_building_redraw_costandtest_animation_continuityupdated to assert the new contract (draw-once) rather than the capped-redraw oneDone. No animated node re-runs
_draw()any more.What changed
New
NeonPiece- one movable piece of neon art, rasterised once, thereafter animated only throughposition/rotation/scale/modulate/visible. Buildings are now assembled from these.Mothership: the four satellites are child pieces inside a holder; the orbit is the holder's
rotation, so eight hexagons a frame became one transform. The breathing core is drawn at a reference radius andscaled.Ship crafter: hull parts are four children whose attachment is a
visibleflip and whose weld flare ismodulate.a; the launch is the hull holder'spositionand alpha; the crane rides the rail onposition.xwith the hoist stretched byscale.y; the completion ring is drawn once and scaled outwards; beacons and reactor cell fade viamodulate.a.Projectile: the bolt is now a fixed shape with no
agedependency, drawn once.Explosions: each particle is its own child drawn once and moved; the whole effect fades by setting
modulate.aon the parent, which CanvasItem inherits down the tree - so one assignment fades every particle.The 30Hz cap and the
BuildingStaticLayerfrom #6 are both gone, no longer needed.On the sparks
The weld sparks were the one element flagged as maybe-unconvertible. They are now four spokes on a holder that spins and scales, which reproduces the flicker without re-tessellating anything. No redraw needed after all.
Note on approach
I did not reach for AnimationPlayer resources. The crafter's cycle is conditional (which part is attached, whether it is welding, flashing or launching), and that logic stays clearer and testable as code; the properties are assigned from
_process, which costs nothing because assigning a transform does not trigger a redraw. The saving that mattered was the node decomposition, exactly as the ticket described. Happy to move to AnimationPlayer tracks if you would rather have the animation authorable outside code.Verified
test_building_redraw_costrewritten for the new contract: after 2.5s of animation the crafter, mothership AND command centre each report draw_count == 1, while the crane has travelled and the satellites have orbited - so it cannot pass by freezing the animation._vfx_previewscene and looked at them. This mattered: bolts once went invisible after a similar optimisation and headless tests did not catch it, because--headlesshas no GPU. Six bolts and four explosion types all visible.One bug caught during the work: the hull was initially drawn at the building's origin instead of in the cradle, because the holder was offset by BAY_CENTRE when its child pieces were already local to it. Caught by looking at a render, not by a test.