Blockbuster 767
README.md
SoundDirector
The sound of BLOCKBUSTER 767 — the whole of it, in one component on
mount_sound.
It plays a music bed and a playroom ambience that loop end to end, rides the
wind-up motor and the air over the airframe off Game.telemetry, fires the
one-shot library from the event bus, sets the mixer levels, and gives the Kid
a voice with the words on screen.
What it needs
game/audio/clips/*.soundClip— baked bygame/audio/bake.luaufromgame.audio.recipes. Pure synthesis; costs no credits, cannot fail.game/audio/voice/*.soundClip— bought ONCE fromaudio_gen.speechbygame/audio/voicebake.luau, one clip per line ingame.audio.lines.- An
AudioListenersomewhere. The scene puts it on theCameraRig, which is what makes a spatial hit at a block come from where the camera is watching.
A clip that is missing is not an error: the sound is skipped and, for a voice line, the caption is still shown.
Playing a sound from another slice
local sound = entity.find("mount_sound").component.get("SoundDirector")
sound:play("hit_heavy", { position = pos, volume = 0.8, pitch = 1.1 })
sound:say("topple") -- the Kid, from the pool for that cue
play(name, opts) takes any name in game.audio.recipes: launch, whoosh,
windup, hit_light, hit_heavy, collapse, clatter, snap,
wreck_sting, score_pop, ui_tick, ui_confirm, ui_back, star_chime.
With a position the sound is spatial; without one it is flat. Each family
carries a cooldown, so forty blocks landing at once is a collapse rather than
forty overlapping ticks.
Nothing HAS to call either of these: the bus already drives them. launch,
impact, partLost, blockDisplaced, blockOff, towerToppled, score,
wreck, sessionEnd, pause and state each have a sound, so a slice that
emits its events correctly is heard without doing anything else.
The captions
Every spoken line is also written down, through game.audio.caption:
local Caption = require("game.audio.caption")
local now = Caption.current() -- { id, text, remaining, duration } or nil
local off = Caption.on(function(c) ... end) -- fires on every new line
SoundDirector draws the words itself, in a band above the bottom control band,
proportional to ui.screenSize(). The presentation slice can take that job
— carry the line inside a composed card instead:
Caption.claim("bb_screens") -- SoundDirector's band stands down
Caption.release() -- hand it back
The band is registered at UI layer 10, below every authored screen, so it never takes a tap that belongs to a card or a HUD button.
The mix
Channels: music, ambient, sfx, engine, voice, ui. The public fields
set each one; change one and call :refreshMix(). Every clip was baked with
headroom, so nothing in the library reaches the mix above about 0.77 peak on
its own.
:report() is the read: the master levels, how many sources are audible, the
engine gain and pitch, the caption, and the mixer state.