Skip to content

Trigger object SFX on physical impact with force-scaled volume#5

Open
dalision wants to merge 1 commit into
neilsonnn:mainfrom
dalision:feat/collision-triggered-impact-sfx
Open

Trigger object SFX on physical impact with force-scaled volume#5
dalision wants to merge 1 commit into
neilsonnn:mainfrom
dalision:feat/collision-triggered-impact-sfx

Conversation

@dalision
Copy link
Copy Markdown

Problem

Object impact SFX in worlds/<slug>/output/<object>/sfx/ currently only play once, at the moment of click-to-grab (useObjectGrab.tsplayInteractionSfx). Actual physics collisions — object hitting the floor after a throw, smacking into a wall, knocking into another object — fire no sound at all.

This makes the impact samples (recorded by the SFX pipeline specifically to sound like physical contact) feel disconnected from the physics: you can chuck a chair across the room, see it ragdoll, hear nothing on landing.

Change

SceneObject.tsx wires Rapier's onContactForce on each dynamic RigidBody. Three guards keep the audio usable:

  1. Force threshold (IMPACT_FORCE_THRESHOLD = 8) — skips below-threshold contacts so an object resting on the ground stays quiet.
  2. Throttle (IMPACT_THROTTLE_MS = 200) — at most one playback per object per 200ms, so a bouncing/sliding contact stream doesn't machine-gun the same sample.
  3. Force-proportional volumevolume = totalForceMagnitude / IMPACT_FORCE_FULL_VOLUME, clamped to [0.15, 1.0] so light taps stay audible while hard impacts hit max.

playRandomSfx gains an optional volume argument (defaults to 1) so the existing click-to-grab path is unchanged.

Net diff: +17 / -2 in one file, no new dependencies.

Verification

  • bun --cwd=app run typecheck passes
  • Manually validated on a fresh `cozy-room` world with two rigidbody objects (wooden chair, potted plant): click-to-grab still plays a sample (no behaviour change), and tossing them into the floor / walls / each other now triggers an appropriately scaled impact sound from the per-object sample pool. Light landings sound soft, hard throws hit max volume, resting-on-the-ground silence is preserved.

Notes

  • The three constants are conservative defaults — could move to `useDebugStore` if you want per-session tuning via Leva.
  • `IMPACT_FORCE_FULL_VOLUME = 80` was calibrated by feel against Rapier's default solver settings; might need re-tuning if anyone changes `linearDamping` / `additionalSolverIterations`.
  • Intentionally does not differentiate impact target (floor vs wall vs another object) — would need a material lookup table for that.

Previously object impact SFX only played on click-to-grab — the
samples in worlds/<slug>/output/<object>/sfx/ never fired from
actual physics collisions (floor, wall, object-on-object).

This wires onContactForce on each dynamic RigidBody and:
- skips contacts below a force threshold so resting bodies stay quiet
- throttles repeated impacts to one playback per 200ms per object
- scales volume linearly with totalForceMagnitude, clamped to
  [0.15, 1.0] so taps stay audible and hard impacts hit max

playRandomSfx now takes an optional volume arg so the click path
keeps its existing full-volume behaviour with zero changes at the
call site.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant