the symptom, in your words

"My custom sound event won't play"

✓ verified on 26.07.22
lane Audioposted updated

▸ SYMPTOM

  • Sound.Play(...) silent; log may say Couldn't find sound event.
  • .sound_c / .vsnd_c sit healthy on disk.
  • Partial paths copied from folder layout ("impact/boing_a") never work.
  • Sounds compiled after the session started stay invisible until an editor kick.

▸ CAUSE

SoundEvent.Find (decompiled contract, engine ~26.07.08c) only accepts two name forms:

  1. Full resource path WITH extension: cache-hash lookup on ResourcePath including .sound.
    Example: "sounds/impact/boing_a.sound"
  2. Bare filename: fallback compares ResourceName (never contains /).
    Example: "boing_a"

Resource.FixPath only normalizes slashes / strips _c. It never prepends sounds/ or appends .sound. So "impact/boing_a" fits neither form.

The install's Sound.Play("cardboard_rustle_loop") works via form 2: generalizing that to "family/name" is wrong.

Also: the editor builds its asset registry at session start. Assets compiled later exist on disk but won't resolve until re-index (editor kick). Mtime staleness is a red herring. Prove with a known-working control asset at identical mtimes before chasing clocks.

The miss is completely silent (re-verified 26.07.22): a name that fails to hash-match doesn't throw. Sound.Play just logs Couldn't find sound event, returns no handle, and a null-tolerant caller degrades straight to silence with no stack trace anywhere in the chain. Comparing asset_info on the failing name against a known-working one shows them identical in every compile/mount respect, which isolates the path string as the only variable, so treat the string, not the asset, as the suspect.

▸ FIX

snippet
// GOOD
Sound.Play("sounds/impact/boing_a.sound");
Sound.Play("boing_a");

// BAD - never resolves
Sound.Play("impact/boing_a");
Sound.Play("sounds/impact/boing_a"); // missing .sound - still form-1 miss

Translate house names → engine paths at one boundary:

snippet
static string EngineEventPath(string houseName) =>
    $"sounds/{houseName}.sound"; // or your folder layout, always with .sound

Numbered variant suffixes go before the extension: "sounds/impact/boing_a_2.sound", never "...boing_a.sound_2" (not a form the resolver recognizes).

A library-mounted event needs no library-name prefix: reference it with the same project-relative sounds/... path a project would use for its own assets, extension and all.

Don't trust "Couldn't find sound event" to mean missing/uncompiled: distinguish name-form bugs from index/compile bugs (live editor probe if you have one).

After adding sounds mid-session: kick the editor / restart play so the registry picks them up. Zero-volume canary of a few known events at first SFX use turns "audio mysteriously dead" into one log line.

Retry policy for loops: once after ~2s, then disable, never per-frame retry a permanently missing event.

▸ WHY IT WORKS

Resource lookup is a hash on the exact path string or a bare-name equality check. Partial paths are a third shape the API does not implement. Session-start indexing means disk presence ≠ runtime visibility until the registry refreshes.

Verified on engine 26.07.22: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Re-verified on 26.07.22: the miss never throws (a null-tolerant caller degrades straight to silence); asset_info on a failing vs working name is identical, isolating the path string; a library-mounted event needs no library-name prefix; variant suffixes go before the extension.
  • Published

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord