Unity Workflow LibraryUWLAbout

Topic 5 · DOTS

Gameplay simulation patterns (DOTS)

Crowd movement, sensing, target selection, spawn placement and match flow on ECS.

Sim systems built on top of ECS: crowd movement, AI sensing, target selection, spawn placement and match flow.

  • PRESENTATION-Y-LIFT-IS-THE-COMBAT-HEIGHT — if sim stays on the XZ ground plane and presentation adds a hover Y, muzzle, projectile hit capsules, aim points, and death pose must use that same lift. Otherwise shots leave the feet, capsules miss the visual body, and corpses hover for the whole death window. Drop the hover on death (then shared burrow), don't keep applying it while DeathState != 0.

  • SPAWN-QUANTIZE-COINCIDENT — a spawn/formation resolver must NOT snap valid points to nav-cell centers, and any separation force needs an ASYMMETRIC zero-distance fallback. Two paired traps that together make "N units spawn but N-1 are visible, forever": (1) a placement helper that returns the walkable CELL CENTER even when the desired point's own cell was already valid quantizes every spawn to the grid — formation offsets smaller than a cell (1.15 m spread on a 2 m grid) collapse onto the SAME center, spawning units perfectly coincident. Return the exact desired point when its cell passes; snap to centers only while searching outward from a failed cell. (2) Coincident units then never separate when the avoidance/separation fallback at distance≈0 is a CONSTANT direction — both get the identical push AND identical steer (same cell), so they march in lock-step for life. Derive the fallback from per-agent identity (index/entity hash, e.g. golden-angle by index) so any overlap breaks symmetry on the first tick. Also: a placement "last resort" return must still be walkable-checked (ring-search) — a blind pos + fwd * K can land inside a blocked stamp and read as "unit spawns but never moves". (Seen in a mobile strategy game: every 4-unit wave rendered as 3 — the two middle units resolved to one cell center and marched stacked through whole matches.)

  • ABILITY-LADDER-SUPERSEDE - a research ladder that grants a per-TIER ability must declare which tier it replaces, or a fixed-capacity loadout silently drops the strongest tiers. The shape: Critical Strike I...V where each level's upgrade grants its own ability id into a DynamicBuffer<AbilitySlot> capped at N (8 is typical). Grant logic that only APPENDS (and skips duplicates) means one 5-level track eats 5 of the 8 slots - research two ladders on the same unit and the later, HIGHER tiers hit the cap and are dropped with no error. The unit keeps tier I forever while the shop happily sells II-V, which reads as "the upgrade did nothing". Fix is an explicit authored SupersedesAbilityId on the ability: at spawn, drop any collected id that a higher tier supersedes; at live grant, OVERWRITE the superseded slot instead of appending. Do not infer supersession from "same EffectKind" - two distinct actives legitimately share a kind (Blizzard and Chain Lightning are both DamageRadius). And when several tiers CAN coexist, make the consumer take the best row rather than stacking them. (a mobile strategy game, 9-track x 5-level tower academies across 7 races.)

  • DAMAGE-PROC-DEFERRED-DRAIN - on-attack procs that touch a THIRD entity (lifesteal, splash) are recorded in the damage job and resolved in one ordered drain system; only the procs that modify the CURRENT event (crit, evasion) belong inline. The damage pass is a parallel job over VICTIMS, so healing the attacker or damaging the victim's neighbours is a cross-entity write two parallel indices can race on (many victims share one attacker). Enqueue a proc credit into a NativeQueue<T>.ParallelWriter owned by the damage system's SystemHandle and drain it in a system ordered [UpdateAfter] - the same producer/consumer split structural changes already use. Two details that bite: (1) a splash-produced DamageEvent must carry a FromProc flag or the next tick rolls the attacker's procs on it again and splash feeds itself forever; (2) roll evasion ONCE per victim per tick, not per event, or a volley re-rolls the same dodge N times. Attribution stays with the attacker so kill bounty still pays. (a mobile strategy game.) (3) QUEUE-CONSUMER-COMPLETE-PRODUCER: a raw NativeQueue is NOT a component type, so the ECS dependency system cannot chain the producer job to ANY consumer — even one in another system that fetched the queue off a singleton/SystemHandle. Every consumer must explicitly complete the producer before its first touch (even a Count read trips the safety system): state.WorldUnmanaged.ResolveSystemStateRef(GetExistingUnmanagedSystem<ProducerSystem>) .Dependency.Complete;. [UpdateAfter] ordering does NOT complete jobs, and GetSingleton on the wrapper component only syncs the component copy, not the container inside it. The symptom is a per-tick InvalidOperationException flood ("previously scheduled job X writes to NativeQueue") that only appears once combat actually exercises the queue. (a mobile strategy game: husk-spawn queue, ~1000 exceptions/min; the sibling proc-credit queue had the completion and never flooded.)

  • MATCH-LAUNCH-SINGLE-FLIGHT — do not CreateServerWorld / Listen on the menu click. World create reloads UITK and a second click Dispose+recreates the listen-server mid-bootstrap (ObjectDisposedException on catalog buffers). One StartHostLocal / StartHostRelay from the load routine; extra clicks no-op while that load is in flight. See the scene flow page same id.

  • CROWD-SEPARATION-TWO-CHANNEL - crowd avoidance needs a soft steering channel AND a hard overlap channel; one signal cannot serve both, and the seam that hides it is the anti-seek projection. The safe march recipe (cap the boids push under unit speed + project away its anti-seek component so a crowd can never reverse the lane and eject units off-map) has a structural blind spot: units steering at the SAME point - a shared spline/file carrot, one chase target - need exactly the anti-seek push the projection deletes, so they compress straight through each other; and any hold state that early-returns (Engage stop, arrived goal hold) discards the avoidance write entirely, freezing the stack in place for the whole fight. Fix shape: the avoidance job outputs TWO fields - soft AdjustXZ (wide ring, strafe bias, lateral-projected in march, unchanged) and hard SeparationXZ that fires ONLY while radii actually interpenetrate, is pure radial, unprojected, capped below unit speed, and is applied in EVERY move mode (march adds it to desired; Engage/goal holds integrate it with the same walkability step-refusal). Overlap-gating is what makes anti-seek safe: strength decays to zero at contact distance, so it can slow a march into a body but cannot build an ejection velocity. Two stability details: (1) enemy pairs resolve at a FRACTION of summed radii (~0.6) - melee chase deliberately stops deep inside reach, so a full-radius pushback vs the unit's own target ejects it out of attack range and flickers Engage<->March forever; (2) coincident pairs need the per-identity golden-angle fallback direction or both take the same push and never part (see SPAWN-QUANTIZE-COINCIDENT). Verified by pairwise census in a live 45-unit brawl: deep overlaps (>50% of contact) at zero, shallow contacts transient. (a mobile strategy game, lane waves stacking on the spline carrot and inside melee balls.)

  • CROWD-GOAROUND-THIRD-TERM - two avoidance channels are still not enough: a blocked mover needs a TANGENTIAL go-around term, and the holding front rank must YIELD less than movers. Amendment to CROWD-SEPARATION-TWO-CHANNEL, from the very next playtest: with soft steering + hard overlap separation + surround slots all working, units still "pushed each other from the back" - because radial separation is force resolution, not navigation. A mover whose corridor to its carrot is blocked by a stopped body has no signal that says go AROUND; its soft push from a dead-ahead blocker is almost pure anti-seek (deliberately projected away in march), so it leans forward into the blocker's back - and the symmetric hard push meanwhile bulldozes the engaged rank into the fight. Two additions close it: (1) go-around - when a HOLDING body (engaged, or steer ~ own pos) intrudes a mover's forward corridor (lateral < summed radii, ahead < ~2.5x summed radii), add a push PERPENDICULAR to the seek direction, side chosen by the blocker's lateral sign (dead-center ties break by entity index); perpendicular means the anti-seek projection preserves it, so the unit actually curves. Clamp the lookahead below the carrot distance or the final approach orbits the very body its surround slot hugs; react to HOLDING blockers only or same-speed column marches weave. (2) asymmetric yield - a holder receives only ~25% of a mover's hard push (holder-holder and mover-mover stay symmetric), so the front rank stands and queue pressure resolves as flow around the flanks. Census note: once a waiting ring exists, "same bearing, one behind the other" pairs become LEGITIMATE (rank formation); the metric that still means pressing is contact-range overlap, not bearing alignment. (a mobile strategy game, same feature, one playtest after the two-channel fix.)

  • AVOIDANCE-NEVER-OVERDRIVES-ANIM-ANCHOR - crowd forces may REDIRECT a march but never push a unit's ground speed above its authored move speed, and an observed-velocity anim driver needs error-proportional smoothing - otherwise units near crowds park their Speed param astride a blend-tree anchor and the walk anim visibly shimmers. The mechanism hides in the composition: soft-push / go-around forces keep their FORWARD component after the anti-seek projection, so a locomotion clamp with headroom (1.5x authored speed) lets crowd pressure ADD velocity - a big unit trailing its own wave cruises ABOVE its authored speed (probe: god at 3.43 avg vs 3.1 authored), wobbling tick-to-tick across the run anchor where the 1D blend tops out AND the stride-scale param kicks in; every crossing flips gait math, which reads as "the walk anim shakes near other units" and is invisible alone (a steady blend ratio looks fine). Constant- tuning the avoidance magnitudes moves the wobble around and can make it WORSE. Diagnose with telemetry, never eyes: sample per-tick raw ground speed + the driver's smoothed param - the tell is raw avg ABOVE authored MoveSpeed with body contacts at ZERO (forces, not blockers, are driving). Fix BOTH owners: (1) clamp the locomotion desired-vector magnitude to authored maxSpeed (direction preserved - redirect, never overdrive; separation-only hold modes are already below it); (2) make the driver's tracking rate proportional to |error| clamped to the legacy accel/decel caps, so sub-0.3 m/s crowd ripple tracks at a crawl while real start/stops stay snappy. Related fairness fix from the same hunt: reciprocal size split - each side of an avoidance pair yields by 2*otherR/(selfR+otherR) (equal pairs unchanged) so crowds part around big units instead of vibrating them - and facing follows navigation intent (seek + projected soft avoid) only, never the hard separation channel. (a mobile strategy game: undead god "walk shakes near units, worse after tuning"; after both fixes the same probe read 3.10 sd 0.00 in the same near-units condition.)

  • AI-SENSOR-DAMAGE-EDGE - macro-AI threat/state sensors arm on DAMAGE EDGES with a decaying hold, never on proximity to always-trafficked zones or a level-trigger over non-recovering state. Two sensor shapes pin an AI state machine permanently, and the starvation is invisible because the AI "does nothing" (no research, no build orders) while looking busy (panic unit dumps): (1) enemy-proximity radii around defense structures in a lane-pushing game - wave traffic lives inside those radii by map geometry, so "enemies near tower / barracks" is true ~100% of the match; (2) a level-trigger on state that never recovers ("any own structure below X% HP => threatened") - structures don't regen, so one bad push arms panic for the rest of the match. Correct shape: arm on damage-evidence EDGES - LastHit counter pulses (XOR signature over the sensor set, split BY STRUCTURE CLASS so mid-lane barracks chip can't arm base-panic / research-block), HP-drop deltas vs a stored sample - each arming re-stamps a decaying hold timer, and level state (critical HP) only AIMS the response (lane/focus), never arms it. A real push lands its first hit pulse within a second, so proximity early-warning buys nothing. Diagnose with a live probe of the hold timers: a value pinned at its cap across samples minutes apart = a per-frame re-armer, not a real siege. (a mobile strategy game: bots never researched and mass-dumped barracks stock all match; damage-edge rearm verified live - inject fortress drop + rax hit pulse, arm -> decay -> calm.)

  • AI-SENSOR-CALM-COUNTER - the safe complement to AI-SENSOR-DAMAGE-EDGE: a LEVEL-trigger is legitimate exactly when the RESPONSE clears the trigger, and visible durable opponent state (enemy building levels) is the right calm-tier sensor for "react to the opponent's investment". Damage edges alone leave a macro AI blind until the enemy's stronger waves are already hitting its base - the developer reads it as "bots don't defend when I upgrade their lane". The missing sensor is opponent DEVELOPMENT: compare each own lane structure's level to the nearest enemy counterpart facing it (nearest-by-XZ pairs the shared-corridor opponents without hardcoding map topology) and treat the deficit as lane pressure. Why a level-trigger is safe HERE when it pins panic elsewhere: counter-leveling REMOVES the deficit, so the trigger self-clears - unlike critical HP on non-regenerating structures, which never recovers and pins forever. Three guardrails keep it sane: (1) it AIMS calm investment only (score + host-rank bonuses toward counter-leveling that lane, army-tech bump while behind) and never arms panic - damage edges keep owning that; (2) scale the response by the deficit so a widening gap escalates; (3) read only state a human opponent could see on the map (levels, built structures), never hidden purchases - reactivity, not cheating. Durable state also needs no per-tick edge detection: sense it once per think. (a mobile strategy game built the damage-edge side; play-verified the counter-sense - bot counter-leveled the exact facing lane in ~6s while the non-facing bot stayed calm.)

  • TARGET-PRIORITY-FRONT-BAND - a target-priority table needs a proximity-dominance band for melee, or priority + retaliation + keep-target hysteresis compose into "the whole pack drags through the front line chasing one archer". The pathology needs three individually-correct pieces: (1) a priority table (hero 110 > unit 100, or any type preference); (2) a retaliation bonus stamped from a RecentAttacker slot (+15 to whoever shot you - a back-rank ranged unit by construction); (3) keep-current hysteresis that re-applies the bonus while the target stays in leash. Result: a melee unit shot once by an archer scores it above the enemy standing in its face, walks INTO the enemy front line (avoidance stops it, so it deals zero damage while bumping), and the leash keeps the fixation alive because the archer keeps refreshing RecentAttacker. Reads as "units fixate on ranged units instead of fighting what's in front". Fix shape: a FRONT-FIGHT band - any valid candidate inside max(attack.Range, MeleeFrontFightRadius) + target pad gets a bonus that DOMINATES the whole table plus every stacked bonus (+1000 vs a 30-125 table), applied in BOTH the acquire scan and the keep-current re-evaluation (only one side = either never engages the blocker or never lets go of it). Within the band the normal table still orders (an in-band hero beats an in-band unit; hysteresis still prevents thrash between equal in-band picks), and the unit may still INTEND the distant pick - it just fights whatever is at hand on the way. Melee-only by default: ranged units don't move to shoot, so priority inside attack range is fine (the ranged variant - band = own attack range so a ranged unit never walks while something is shootable - is a separate decision, it changes hero-focus behavior). Verify with a live pairwise probe, not by eye: for every melee unit whose target is beyond the band, assert no valid enemy stands inside it (a mobile strategy game: 63-69 melee across two matches, 0 violations post-fix).

  • SURROUND-RING-VS-FOOTPRINT - melee surround-slot systems assume POINT targets; a footprint target's ring slots land inside its own nav stamp and the waiting ring parks attackers permanently out of reach. Two facts collide at buildings: (1) blocked-footprint stamps quantize OUTWARD to whole nav cells (a cell is blocked if the footprint touches it), so blocked ground extends up to one cell past the authored half-extent; (2) surround ring radii derive from stop distance (attack range measured to the footprint SURFACE), so ring-1 slots sit inside the stamp - every slot fails the walkable check - and the ring-2 "wait for a slot to free" fallback sits OUTSIDE attack range, where nothing ever frees it (slots free on attacker deaths; a building's ring has none). The unit arrives at ring 2 and stands forever: not engaged (out of range), not moving (arrived), not attacking - until an enemy UNIT enters aggro and retargets it. Alignment-dependent ("usually happens near buildings", never always). Fix shape: footprint targets SKIP the ring system entirely - center steer + walkability step-refusal IS the surround for a wall - and engage/attack reach vs footprint targets gains a stamp-quantization slack (~half a nav cell + body radius) applied IN LOCKSTEP across target acquisition, melee reach, and ranged reach: any one of the three missing the slack recreates the freeze as "engaged but never swings". The slack widens the settle stand-off from walls - keep it a documented tuning const. (a mobile strategy game: "AI just stops attacking or moving until a unit retarget, when a building is around".)

  • PROJECTILE-ARM-EXEMPTS-ORDERED-TARGET - a spawn-protection arm window on a target-ordered projectile visual must still hit-test the ORDERED victim from frame 0, and a forward muzzle offset must collapse to the body axis when the target stands inside its reach. At point-blank (a melee attacker hugging a caster) the bolt crosses its victim entirely INSIDE the arm window (0.06s at ~17 m/s is ~1.1m - wider than a whole hit capsule), then sails past and flies its full life "off to the side" - while server damage (unarmed, target-locked) lands fine, so it reads as a broken attack that no damage log corroborates. Arm windows exist to skip near-muzzle bodies; the ordered target is never a false positive, so exempt it from the gate instead of shortening the window (shorter still loses a frame at low FPS). Companion launch trap: a staff-tip/bow muzzle authored FORWARD of the body can sit at or past a hugging target, so the launch direction reads sideways/backward from camera - when the flat self-to-target distance is inside the muzzle's flat reach + a small pad, launch from the body axis at the authored height instead. (a mobile strategy game: mage autos vs adjacent melee.)

  • KCC-SWEEP-SLIDE-STEP-DEPENETRATE — a kinematic character controller is sweep EVERY leg + slide + step-up + budgeted ComputePenetration depenetration, with movement BLOCKED while embedded. One truncating capsule cast is not a controller: a glancing hit stops the player dead without SLIDE, jagged mesh triangles wedge the capsule on "flat" ground without a STEP, a cast that starts interpenetrating returns distance 0 and freezes the player permanently, and a horizontal-only sweep with y += vertical applied blind tunnels on the axis gravity uses most (one 60 Hz tick at terminal velocity is 0.667 m). "Allow movement while overlapped" as the wedge recovery is the mesh-walk exploit door. Grounded is the persisted flag the sweep WROTE, probed with a sphere (a centre ray lands in crevices and sucks the body down) and true only while DESCENDING — a proximity test reads grounded during the first ticks of a jump. (a survival game)

RADIAL-UP-NEEDS-THE-BODY-CENTRE-NOT-THE-POSITION

  • On a curved world, normalize(position) is only up when the simulation origin IS the body's centre. Any recentred frame — a floating-origin rebase, a per-region chart, a sector local to a patch of surface — puts the origin ON the surface, so the centre is a large offset in local metres. A kilometre from that origin the position vector is nearly HORIZONTAL, and it degrades smoothly rather than failing: it is exactly right at the origin, so a probe parked at spawn looks correct.
  • Up is normalize(position - centreInLocalMetres), read from the frame the entity belongs to. The frame already carries that centre; nothing should re-derive it.
  • Symptom shape when this is wrong: the tangent projection strips the wrong component so movement leaks into the radial axis, a ground settle pushes bodies sideways into terrain, and a ±N m ground raycast fires ALONG the surface and reports no hit — so a spawn gated on ground support places nothing at all and the feature is silently absent rather than visibly broken.
  • The fix is one owner for the formula. If the helper that has it takes an EntityManager, a Burst job cannot call it and every job writes its own copy; give the helper a plain-data overload taking the frame, and have the manager version forward to it.
  • Audit by grepping for a normalize of a raw position (normalize(position), normalize(self), normalizesafe(transform.Position, up)). Measured on one lane: four jobs and a spawn probe.

REBASE-CARRIES-WHAT-THE-ENTITY-RIDES

  • A floating-origin rebase or region transfer keyed per character must move the vehicle the character sits in, in the same commit, or refuse to start. A seat that copies the vehicle pose into its occupant every tick fights a transfer that moves the occupant alone. Measured on one server: a seated driver placed 14 m past the 2,048 m rebase line was re-prepared every ~0.6 s (22 times in 14 s) and never committed. Both poses froze, the occupant's region changed about 20 times without its pose moving, and the vehicle stayed in the old region.
  • The refusal that should have caught it checked only for a physics parent, and a seat is not a parent. Make "is riding something" its own gate, or make the vehicle root the transferred unit and carry occupants and cargo with it.
  • Test a seated crossing, not only a walking one. A scripted vehicle driven at the line wanders and wedges on terrain; place it past the line to prove the transfer path, and record the driven crossing as unproven.

LIVE-KEY-NEVER-FROM-A-LOSSY-DESCRIPTOR

  • Never rebuild a live object's identity key from a stored description of it; look the live object up by the handle you already hold. A region registry keyed frames bit for bit on a cell-plus-offset origin. Boarding rebuilt the frame from its saved descriptor, which kept the origin as double meters, and at planet distance the round trip dropped offset bits. The registry refused "a live frame with different canonical metadata", and the catch disconnected the player. A model of the conversion failed for 10000 of 10000 sampled origins.
  • Lease or join by the membership or handle the caller holds. Keep descriptors for persistence and for creating a new frame, never for finding an existing one.
  • A thrown prepare step that has committed nothing releases and latches (logged once, no retry loop). It never disconnects the client.

RANGE-CONTRACT-USES-EFFECTIVE-GEOMETRY

  • A promise that one weapon can answer another must compare effective distances from the same origin and plane, including target footprint and reach allowances. Comparing two authored range numbers alone cannot prove it.
  • Scope collision-clearance exceptions to the attackers that need them. Keep acquisition, release and burst/volley checks on the same minimum/maximum band; present those resolved values in the UI.
  • Regression evidence: a 6.5 m ranged attack plus 1.65 m footprint and 2 m melee clearance stopped about 10.1 m from a tower with a 9 m weapon. Removing the inapplicable ranged allowance restored reach without widening tower balance.

AMBIENT-WANDER-IS-A-DESTINATION-NOT-A-BEARING

  • An idle agent that redraws a uniform heading on every decision does not wander, it paces. A uniform bearing has a MEAN turn of 90 degrees against the heading it already had, and half of all draws exceed that. At a decision cadence of 0.4 to 0.7 seconds — normal for a cheap utility brain — that is a body reversing several times a second. It reads as broken to anyone watching, and no amount of turn-rate limiting in the locomotion hides it, because the intent itself is noise.
  • Wander is a destination held until it is reached: a leg of an authored length, on a heading turned by at most an authored angle from the CURRENT one. Correlating the new heading with the old is the whole fix; the leg length and the turn ceiling are what make one species hold a line and another dart about, from the same code.
  • Most legs should end in a pause, not another leg. An undisturbed animal, civilian or idle unit spends most of its time stopped, and a stop is what makes the movement between stops read as purposeful. Author the chance and a seconds band per kind.
  • A leg needs an abandon timer as well as an arrival test, or a body walking into geometry holds a destination it can never reach. Size it from the leg length at the gait it will use, with slack for avoidance detours — not a constant.
  • Deriving the bearing from a hash of (identity, decision tick) instead of storing it does not avoid any of this. It is still a fresh uniform draw per decision; it is only a reproducible one.

OPPOSED-BEHAVIOURS-NEED-A-SHARED-BOUND

  • Two behaviours that pull opposite ways will oscillate unless one of them is clamped inside the other's threshold. Cohesion pulling a member back to its group and wander pushing it outward is the canonical pair: wander aims out, cohesion fires, the body returns, wander aims out again. The visible result is identical to random-bearing pacing, so the two defects mask each other and fixing one alone leaves the symptom in place.
  • Clamp at the SOURCE: the wander destination is clipped to the cohesion radius when it is chosen, so cohesion never has to fire for a self-inflicted excursion. One authored radius, not two.
  • The general test: for every pair of behaviours where one's success condition is the other's trigger, the first must be unable to cross the second's threshold on its own.

ONE-DECISION-ONE-CLOCK

  • A decision guarded by two timers ANDed together is guarded by the shorter one. The shape if (committed && now < nextThink) return; reads as "held for the longer of the two" and behaves as "held for the shorter": once the think timer fires, the commitment is re-scored and can flip, so a 2.5 second commitment inside a 0.5 second think cadence never held anything. It is a silent defect — the field exists, is written, is read, and does nothing.
  • The two clocks mean different things and must be applied separately: the think cadence decides HOW OFTEN the agent looks, the commitment decides WHETHER it may change its mind. Gate them in that order and both keep their meaning.
  • Holding a decision must not hold its AIM. A chase committed for two seconds against a target position captured two seconds ago is a predator running at where the prey used to be. On every think, re-read the target's position for the action already chosen, and leave alone the actions whose target IS the decision (a roam destination) or which have no target (a pause).
  • Debug readouts must show what the agent DID, not the candidate that won. If the winning candidate is post-processed into something else, relabel it in the readout; a score table disagreeing with the body next to it costs someone an afternoon.

A-PHYSICAL-BODY-IS-NOT-AN-OBSERVER

  • OBSERVER-IS-A-ROLE-NOT-A-POSITION (HARD) - reusing the streaming/AI observer marker so a spawned body keeps its ground loaded makes every spawn open its own streaming ring and re-trigger every refresh keyed on observer count. Measured: each dropped item cost 20-30 ms for several frames after it landed, with three terrain chunk generations per drop. The marker had been added for one honest reason - the chunk under the item must stay collidable or it falls through - but the systems downstream read it as "a viewer is here": the streaming system opened a Chebyshev square of max(viewRadius, collisionRadius) chunks around it plus look-ahead from its velocity, and a trunk-collider system and a census system re-ran their whole refresh because the observer COUNT changed. After splitting the role: zero generations in 705 frames, frame time flat.
  • Split the marker, do not scale the radius. A support marker whose sample span is 0 keeps exactly the chunk the body rests on - no ring, no look-ahead, no census, no AI activation - and the viewer marker stays with players. Put the span in a pure function (Span(supportOnly, viewRadius, collisionRadius)) so a test pins it with no world.
  • Every system keyed on observer COUNT is the blast radius. Enumerate them from the marker's own declaration before adding a second observer kind; a count-triggered refresh turns "one more observer" into a full rebuild, which is why the cost appeared on the frames AFTER the spawn.

CLAMP-DEMAND-TO-THE-ENVELOPE-BEFORE-ALLOCATING

  • An actuator allocator that minimises a weighted force + torque error buys extra force with torque error when the demand is impossible. Measured on a thruster-driven hull whose main engines sit off the centre of mass: the velocity loop asked 628 m/s2 of a 30 m/s2 ship at full throttle, and 20 s of "fly straight and hover" ended at -103 m/s vertical, tumbling. Clamping the desired acceleration and turn acceleration per axis to the measured envelope first: 220 m/s, 0 deg/s.
  • The envelope is the largest pure push (or turn) per axis the same allocator reaches with the other axes held at zero, found by bisection. Rebuild it when an actuator is lost or restored, never per tick; a lost thruster then shrinks the envelope instead of spinning the hull.
  • Measure with more solver iterations than the tick uses. At 12 coordinate-descent sweeps a damaged hull's 3.3% torque residual failed a 1% test at every scale and read as "cannot go forward" (true value 29.8 m/s2 at 200 sweeps); in flight the rate loop absorbs that residual.
  • Start the per-tick solve from zero throttles, so a predicted client's replay needs no stored solver state.
  • Time the rebuild, not only the step . Measured on a 22-thruster hull: the per-tick solve cost 3.2-3.3 us in a Burst job, while the envelope rebuild (64 sweeps, 12 bisection steps, twelve axis directions) took 20.5 ms managed, longer than a 60 Hz tick. A volley that damages ten ships would stall the server. Rebuild in a Burst job off the tick that applied the damage and fly on the old envelope until the new one lands; gate the rebuild time in the same test as the step.

A-TIMED-STATE-NEVER-SAVES-AS-ITSELF

A server state that a clock ends (bleeding out, a stun, a burn, a respawn lock) is saved as where the clock leads, never as the state itself, because offline time never runs the clock. Measured on a downed state with a 60 s bleed-out: a body saved as downed would come back downed after a logout, a crash or a server restart, with a fresh timer or none. Logging off then works as an escape, and a restart turns into a free revive. The fix:

  • Save the outcome. The downed player saved at shutdown or on leaving comes back needing respawn. A revive saves again, alive, with its low health.
  • Give it its own flag when the outcome's side effects have not happened yet. "Dead" there meant the loot was already on a corpse; a downed body still carried it. Folding downed into dead would lose the backpack. A separate flag lets the offline-body owner finish the death properly, moving the loot onto a corpse the moment the player leaves.
  • Trigger a save when the state changes, not only on the periodic save, or a crash between the two brings the body back standing.
  • Test the restart path for both the timed state and its clean exit (downed -> respawn needed; revived -> alive with its health).

SHARED-CAP-DRAINS-BY-PRIORITY

  • A first-come per-frame cap shared by several event topics starves whichever topics flush last. Measured: presentation topics flushed in fixed bus order, so early high-volume topics (impacts, deaths) spent the per-frame view cap, and later ones (crit, shield break, level-up) played nothing in busy frames although their authored rows existed. Queue requests during the flush and drain them by an authored per-topic priority afterwards; gate frequent topics with an authored cooldown. Test at the cap, not in a quiet frame.

STICK-DEPTH-FROM-THE-RENDERED-BODY

  • Embedding a stuck projectile relative to the shared gameplay hit capsule floats it on thin bodies and sinks it into bulky ones. Measured over 108 unit looks x 72 rays: infantry torsos sit 19-25 cm inside a 0.40 m capsule, so a 0.12 m embed left tips 7-13 cm outside the skin (about 90 % outside), while horses are wider than the capsule.
  • Author a presentation body radius per unit type (the median radial skin distance from the capsule axis, which ignores held weapons and arms). Intersect the flight ray with that visual capsule, because an oblique ray crosses more than hitR - visualR; add the embed; clamp at the ray's closest approach to the axis. Result: 1 % outside, tip p50 19 cm in (17 % of the shaft). Gameplay hit radii stay unchanged.
  • An open vehicle (catapult, cart, frame) is mostly air, so no radius works. Author solid-part boxes per vehicle (oriented boxes in model space, an authored cap per unit, one validator shared by the inspector, the authoring tool and the entity builder). At impact only, slab-test the flight line against them, embed from the first box entered but never past its middle, and move a shot through a gap sideways onto the nearest part. Measured: 58 % of tips outside the mesh before, 0-1 % floating with 8 boxes.
  • Propose boxes from a voxelised solid (fill inside along two axes so open meshes do not streak), then greedily carve the all-solid box that covers the most uncovered volume. Boxes fitted around mesh islands, or merged part OBBs of spoked wheels, stay half empty. Score "tip inside" with an entry/exit depth counter along the line; nearest-face tests break on interpenetrating parts.
  • Measure offline: an Edit-mode probe that bakes each look's posed mesh into a collider and runs the runtime stick formula over a fixed ray set covers every look, while Play catches a few arrows before their targets die.

CROWD-SLOTS-PROMOTE-THROUGH-THE-OPEN-STRETCH

Reserved attack and queue rows around one structure (a crowd attacking a tower or gate). Two lifecycle bugs appeared only under attrition; see OPPOSED-BEHAVIOURS-NEED-A-SHARED-BOUND for the engage gate (engage only while holding an attack slot).

  • One row per step empties the front rank. Measured: attack slots in use fell from 51 of 51 to 0-5 once the structure killed faster, while 141 queue holders sat 11+ rows out with their cooldowns expired. Promotion moved a unit one row per step (walk plus cooldown), and that latency across 11 rows exceeded an attacker's life. A ready holder moves through the whole open stretch in one step: melee to the innermost free row, ranged to the outermost row within its reach.
  • "Join the back of your sector" must count only rows between the unit and the target. Counting every claimed row in the arc, including rows behind the unit, left 111 units slotless at 4-10 m, standing inside the rows, because no row qualified (they are also never sent more than one row outward). Back = the outermost claimed row within the unit's own distance.
  • Verify every crowd or queue fix under high attrition. Both bugs were invisible with a weak weapon (51/51 slots used at 950 units): churn only happens when attackers die fast. Each probe run includes a high-kill leg, records kills per second beside the result, and gates on a per-row histogram of slot kind and holding state.
  • Only claims of the unit's own blocking kind count as "the row in front" (added). A ring of another kind, such as a ranged firing row, blocked melee promotion when any claim counted: measured 0 melee units within 8 m while 245 ready holders waited behind 45 archers.

BENCHMARK-STATES-ITS-SCENARIO

  • A synthetic job benchmark needs a Play-realistic clock and event rate. Measured: a 1 s integer clock hid a 0.35 s per-slot refill cooldown (full refill every tick), and 10 % of attack-row deaths per 30 fps frame (about 150 kills/s, where Play peaked near 2 kills/s) invented a throttling that never happens. Take the clock and event rates from Play numbers and report the scenario beside the time.
  • The first Editor Burst timing of a job can read about 4x slow (0.52 ms against 0.16 ms at 600 units, same job, cause not isolated; likely first-run compile or cache state). Warm up, repeat, and report the Burst safety-check state with the number.

A-GHOST-WITHOUT-VELOCITY-IS-STILL-A-BODY

  • Classify what a shot or blade struck by what the entity IS, never by PhysicsVelocity. Measured: a hitscan and a melee sweep labelled a hit "dynamic body" only when the entity had PhysicsVelocity; server-moved kinematic ghosts (animals: colliders, no velocity) all came back "static" with victim ghost 0, alive or dead, so the client drew a rock impact, left the decal on the world and showed no body-hit feedback, while damage (keyed on the hit entity) still landed, which hid it. The projectile path of the same weapon already used "any ghost or velocity", so one weapon had two answers.
  • One classifier for every hit path (hitscan, blade, projectile), keyed on what makes a body: a health (or other body) component, or velocity. A test entity with health and no velocity must classify as a body.
  • Ghost-ness is not a body (amended). The first fix keyed on "any ghost"; placed building pieces (doors, storage boxes) are ghosts too, so a bullet on a door would read as a body: decal dropped where decals skip moving bodies, and body-hit feedback on wood. A ghost only says the entity replicates. Test a ghost with no health classifies as static. A corpse must keep its health component until despawn, or it stops being a body.