Builtin Library
README.md
shake_engine
Procedural camera shake engine used by the built-in first-person controller. Layered sine waves synchronized to step cycle for head bob, decaying sinusoid for landing impact, and multi-sine pseudo-noise for trauma shake. All shake is additive — it never fights player input.
Pure math — no engine calls, no module state. Consumers (the
fps_controller module) accumulate step time and trauma, then pass
them in alongside the active shake profile each frame.
Exports
M.headBob(t, profile, speed, isRunning) -> (x, y, roll)— locomotion bob.M.breathing(t, profile) -> (x, y, pitch, roll)— subtle idle micro-movement.M.landingImpact(elapsed, fallSpeed, profile) -> number— decaying-sinusoid vertical offset.M.traumaShake(t, trauma, profile) -> (x, y, roll)— multi-sine pseudo-noise scaled bytrauma^power.
Types:
ShakeProfile = { walkFrequency?, walkVerticalAmplitude?, ..., traumaDecayRate? }— see the module body for the full field list.
Usage
local ShakeEngine = require("@builtin::modules.fps_controller.shake_engine")
local x, y, roll = ShakeEngine.headBob(stepTime, profile, 1.0, false)
local bx, by, bp, br = ShakeEngine.breathing(now, profile)
local landY = ShakeEngine.landingImpact(elapsed, fallSpeed, profile)
local tx, ty, tr = ShakeEngine.traumaShake(now, trauma, profile)
Notes
- Output amplitudes are in metres, rotation outputs in radians. The consumer is expected to add them to the base camera pose.
headBobreturns zeros whenspeed < 0.01.traumaShakereturns zeros whentrauma < 0.001.landingImpactreturns 0 when the computed offset's magnitude drops below 0.0005m so the consumer can stop sampling after a few hundred ms.- Frequencies, amplitudes, and decay rates all come from the profile —
fall back to the
realisticdefaults when a field is missing. Seemodules/presetsfor the built-in profiles. - No state, no time-source dependency — pass in whatever clock you're driving the controller with so the shake stays frame-coherent with the rest of the controller.