Lobby dither

Analyzed the physics-engine cost of the dithering system that turns obstacles between camera and player translucent, and measured improvements along two axes — query method and computation cadence: PhysicsFixedUpdate reduced by 0.2–0.4ms, GC allocations reduced by ~30%.
제목
Existing structure
A Kinematic Rigidbody + BoxCollider(Trigger) approach. The collider's size/position/rotation change every frame, so PhysX has to reinsert this collider into the broadphase tree every frame and continuously test against every trigger collider in the scene. As long as the collider is alive, the physics engine keeps working.
Improvement hypothesis
"If we read PhysX BVH on demand only when needed, the broadphase re-insertion cost disappears."
Both OverlapBox and RaycastAll are read-only APIs that immediately query the internal PhysX BVH. Without a persistently registered collider, broadphase updates are no longer needed every frame, and cost is incurred only at the moment of the query.
The computation cadence was also changed. Dithering is an occlusion test, and the camera / player don't move much in a single frame, so a 30-frame interval (~0.5s) is enough.
Measurement (S21 / High setting at 60FPS)
| Method | PhysicsFixedUpdate avg | Peak | GC allocations |
|---|---|---|---|
| Existing collider (per frame) | 0.50~0.90 ms | 0.91 ms @ PL 18ms | 495,748 |
| OverlapBox (every 30 frames) | 0.30~0.50 ms | 0.37 ms @ PL 17ms | 352,203 |
| Raycast (every 30 frames) | 0.30~0.50 ms | 0.48 ms @ PL 16ms | 367,052 |
Analysis — where did the real win come from?
Looking at the numbers, one might conclude "removing broadphase cost was the big win," but in reality the cadence change had the bigger impact.
- If we kept per-frame query under the new method (collider→OverlapBox), savings would have been much smaller
- The 30% GC drop also comes primarily from fewer calls due to the 30-frame interval (495K→352K)
- Measurement noise exists too — single-snapshot comparisons are risky; rely on running averages
A meaningful engineering takeaway: before rewriting the algorithm, reduce call frequency first. The single-query cost in a well-built engine like PhysX is already small, and what looked bloated was actually a cumulative-call-frequency problem.
Decision
Adopted RaycastAll(camera→player direction, distance = segment length) + 30-frame interval.
- Equivalent average performance vs OverlapBox, but a better semantic fit (line-of-sight = ray)
- Per-zoom-level distance / radius settings reuse the
cameras.protodata as-is
Retrospective
- Measurement noise was large — ±0.2ms swings even in the same scene depending on camera work. Need to look at running averages and peaks together.
- Avoided the intuitive trap "collider-based looks expensive, let's switch to raycast" — instead, decomposed the change axes (query method / cadence) and measured each separately.
- The biggest savings actually came from cadence — an honest conclusion that a simple change (one constant) outperformed a structural change.
Implementation
- 01
Existing collider-based (per frame)


- 02
OverlapBox query (every 30 frames)


- 03
Raycast query (every 30 frames)

