slop-stuff cheatsheets & experiments git

slop-stuff / Game dev

GameDev patterns

Patterns that make games tick — herded, not essayed.

Fixed timestep, ECS vs OO, HFSM/BT, pooling, spatial picks, frame budget, and AI-slop kill-list.

gamedevtimestepECSHFSMspatial

Every game is input → sim → draw. Use seconds (dt = 1/60), not MS_PER_UPDATE. Composition ≠ ECS. Fixed dt is necessary for net/replay — not sufficient alone.

Quick reference

PatternOne-liner
Fixed + accumulatoracc += clamp(frame); while acc>=dt: fixedUpdate(dt); acc-=dt then render(lerp(prev,curr,acc/dt))
Spiral fuseClamp frame (0.25s) and/or maxSteps
InputQueue every frame, apply on tick
ECSentity=id · component=data · system=behavior — when thousands of similar things
Stateenum ≤~8 · objects when state owns data · HFSM for shared edges · BT+FSM leaves for NPC AI
PoolFree list O(1) · reset on release · ID+generation, not raw ptrs
EventsObserver = sync · queue = later · don’t emit from handlers
CommandIntent boundary (remap / undo / lockstep) — not particles
SpatialGrid for bullets · quadtree open world · SAP/BVH for physics pairs
Budget16.67ms @60 · 33.3ms @30

Loop / timestep

StrategyDeterministic?UseDeath
Variable update(frameTime)Nojuice / UI / particlessprings explode, tunneling, FPS-feel
Clamped min(frameTime, dtMax)Nosimple 2Dhitch → slow-mo
Semi-fixed (remainder ≠ dt)Almostupper bound on dtspiral; not bit-exact
Fixed + accumulatorYes if rest of sim isdefault for physics / net / replayspiral unless clamp + maxSteps
Fixed + vsync sleepUntil overrunlocked 30/60overrun slows gameplay and render
dt = 1.0/60.0
clampFrame = 0.25
maxSteps = 8
acc = 0.0
prevTime = now()

while running:
  frameTime = min(now() - prevTime, clampFrame)
  prevTime = now()
  acc += frameTime
  steps = 0
  while acc >= dt and steps < maxSteps:
    previous = current
    fixedUpdate(dt)          # physics, cooldowns, AI decisions, net
    acc -= dt
    steps += 1
  alpha = acc / dt
  frameUpdate(...)           # anim, camera, VFX, UI
  render(lerp(previous, current, alpha))

Interp = lerp(prev, curr, α) — needs two states (slerp quats). Extrap = curr + vel*α — rubber-bands on collision. Leftover lag alone is not interpolation.

DomainClock
Physics / cooldowns / AI decisions / netfixed
Inputqueue every frame, apply on tick
Animation / camera / VFX / UIframe

Semi-implicit Euler: v += a*dt; x += v*dt (that order). Box2D-ish: 1/60 + ~4 substeps — never tie the step to FPS.

ECS vs OO

Reach for it whenAvoid when
Classic OO + componentsDomains decouple but you still think in objectsThousands of identical ticking things
ECSMass similar entities / cache-friendly tick dumpsOne-off player systems, UI-as-entities, pre-prototype framework

AoS = object-centric · SoA = system-centric. Archetype = one table per component-set (add/remove moves the entity). Sparse set = O(1) add/remove, slower multi-comp queries.

KEY: Composition ≠ ECS. Hybrid is normal. System order = dataflow; defer structural changes. Don’t boolean-tag until empty chunks. Anti: PlayerJumpSystem for one entity.

State / HFSM / BT

ToolWhen
Enum switch≤ ~8 states
State objectsstates own data
Concurrent FSMskill n×m flag soup
HFSMshared on-ground edges
Pushdownpause / fire overlay
BT supervisor + FSM leavesNPC combat (Halo 2–style)
Player locomotionFSM

Pitfalls: god state · transition spam · BT full-tree tick + LOS at root every frame · BT that’s a 1-depth switch.

Pool / events / command

PatternDoDon’t
Object poolFree list O(1); reset on release; ID+generationO(n) scan; raw dangling ptrs; “pool fixes GC” while refs live
ObserverSync fan-outArchitecture via global EventBus
Event queueCopy payload; drain laterEmit from handlers (storms); stale entity ids
CommandRemap / editor undo / lockstep at intentWrap every particle spawn

Spatial pick

StructureUse
Uniform gridbullets / arena (cell ≈ query radius; half-neighbors)
Quad / octreeopen world, mixed density
Sweep & prunecoherent physics pairs
Dynamic AABB BVHmodern broadphase / rays

n≈20 → nested loop is fine. Two structures is normal. Don’t quadtree the particles. Cell ≪ radius → you still pay near-n².

Frame budget / net

HzFrame budget
6016.67 ms
3033.3 ms

Display can be 30 Hz while sim still runs two 60 Hz ticks. Lockstep = exchange commands, wait for slowest, needs determinism. C/S = server owns state; client predicts own pawn; rewind-replay on correction. Fixed dt ≠ deterministic if RNG, hashmap order, fp nondeterminism, or dropped ticks leak in. Never client-auth position.

AI-slop kill-list

  1. Wrong / variable dt on physics
  2. Physics @ FPS
  3. Explicit Euler for stiff sims
  4. No spiral fuse (clampFrame / maxSteps)
  5. Leftover-lag called “interpolation”
  6. Input sampled only inside fixed update (missed presses) — or only in render with no queue
  7. Gameplay numbers mutated in frameUpdate
  8. “Just use ECS”
  9. Tag explosion / empty archetypes
  10. One-entity systems
  11. Mutate collections while iterating
  12. Undefined system order
  13. Pool everything / no reset / raw ptrs
  14. “Pool fixes GC”
  15. Observer-as-architecture
  16. Event storms (emit from handlers)
  17. Stale queued entity ids
  18. God FSM
  19. BT-as-switch
  20. Spatial cell ≪ query radius
  21. Quadtree on bullets
  22. n² then blame the language
  23. Entity inheritance diamond
  24. Dangling observers
  25. “Fixed dt ⇒ deterministic”
  26. Client-auth position
  27. Command objects in the inner particle loop
  28. Sleep-to-60 coupled as the only timestep
  29. Premature ECS + bus + framework before a looping prototype

Refs