Pooled Prefab Views — Design¶
Goal: Move runtime-generated presentation objects onto authored prefabs, spawned through a shared pool under a tidy hierarchy root. First slice: floaters (also fixes the broken TMP drop-shadow). Same pattern then applies to world labels, health bars, and channel halos.
Why¶
TMP bakes per-vertex SDF padding from the material's outline/underlay state at mesh-generation
time. The runtime floater set its text first (TMP auto-generates the mesh), then
TextFx.ApplyReadable enabled the underlay keyword after — so the mesh had no padded vertices
for the shadow until a later regen ("refresh"). A prefab whose material already has the underlay
baked in generates correctly on frame one. Pooling additionally removes the per-spawn
new GameObject / Destroy churn and organises the hierarchy.
Components¶
ViewPool(DI singleton,Presentation): generic component pool. Owns a—Views—root with per-type child containers (Floaters, laterLabels/HealthBars/Halos).T Rent<T>(T prefab, string container)— pop a free instance orInstantiate(stamping aPooledHandlethat records the source prefab), reparent under the container, activate.Return(Component)— deactivate, reparent to root, push back on the prefab's free stack.PooledHandle: stamped on each instance soReturnknows its prefab/pool.Resources/Views/Floater.prefab+Lato World Text.mat: aTextMeshProusing a material variant of the Lato SDF material with outline+underlay pre-baked (values fromGameConfig.textdefaults). Mesh padding is correct from the first frame.Floatersfacade: holds the injectedViewPool+ the cached prefab.Text(...)rents +Inits; a floater returns itself to the pool at end-of-life.Configure(cfg, pool)also pushes current readability values to the shared world-text material so live tuning still applies.Floater: pooling-awareInit(resetsfontStyle/timer/visibility each rent; no more per-instanceApplyReadable); on life-end callsFloaters.Return(this)instead ofDestroy.
Wiring¶
RaidInstaller:Container.Bind<ViewPool>().AsSingle().PlayerController.Constructgains theViewPool;InitcallsFloaters.Configure(_cfg.text, pool).- Prefab/material live in
Assets/Resources/Views(loaded viaResources.Load, no scene edit, no serialized-ref scene churn).
Labels (done) — rent/return on vision¶
WorldLabel (enemy / boss / rival name tags + ground-loot labels) is now a pooled prefab sharing
the floater's baked world-text material. Owners drive a LabelTag helper that rents a label only
while the entity is in sight and returns it the moment it isn't — so out-of-vision entities hold no
label at all (not merely hidden). The de-flickered Shown state prevents strobing at sight edges;
the rich text is re-applied on rent and whenever it changes (e.g. a rival grabbing the key). Wired via
Labels.Configure(pool) at player spawn. Validated: active labels == shown enemies + near/visible
loot, no leak/duplication.
Shapes views (done) — health bars + channel halos¶
HealthBar (Shapes Line) and ChannelHalo (Shapes Disc) are now pooled prefabs too, rented via
a generic Views facade (no text material needed). HealthBarView became a per-entity DRIVER that
rents a pooled bar only while the bar should show (alive + seen + below threshold) and returns it on
death/heal/fog. The rival's channel ring uses a HaloTag helper that rents on Set and returns on
Hide; the now-unused authored ChannelHalo was stripped from Raider.prefab. Both views parent
their Shapes to themselves so the pool's activate/deactivate hides them. The player's own interaction
ring stays in PlayerVitalsView (a single HUD instance — not worth pooling).
All runtime presentation objects now spawn from the shared ViewPool under —Views—.
Verification¶
Play mode: floater drop-shadow renders fully on the first frame (no refresh needed); floaters recycle (pool reused, no per-spawn instantiate/leak); committed Lato font + underlay values untouched.