A game that stays silent is only half a game, so a sound is an asset like any other (#45)
game-maker / suite (push) Successful in 12m1s

Sounds follow the road fonts already paved: the file goes to the server
content-addressed under its sha256, the project keeps a named asset that
points at it, and nothing is ever re-encoded. The format is read from the
file's own signature, not from what the browser claims - the same .m4a
arrives as audio/mp4, audio/x-m4a or as nothing at all.

Playing is two nodes. "Play sound" takes the asset and may raise or lower
its volume; every call is its own voice, so two jumps in a row are two
jumps heard. Its loop input is a choice of three - from the asset, on,
off - because a checkbox has to spend one of its two positions on "not
set" and would lose a case: an asset that loops could then never be
played once.

Below the graph core stays browser-free: NodeContext hands out an asset
id, BehaviorSystem resolves it against the project and folds the asset's
own volume and loop into the call, and only viewport/SoundLibrary knows
about Web Audio. A run without a sink plays nothing instead of throwing.

The CI browser is muted, so the ear is no witness: the debug hook counts
started and stopped voices, and step 34 reads that counter.
This commit is contained in:
admin committed 2026-09-15 07:54:28 +02:00
1 parent 7babf88636
commit 5671a38044
47 files changed
+2066 -42

No files matched your search

+4 -2
View File
@@ -3,6 +3,7 @@ import type { AssetType } from '../core/model/AssetTypes';
import { findAsset, findObjectHolder } from '../core/model/Project';
import type { ProjectStore } from '../core/store/ProjectStore';
import type { FontStore } from '../core/store/FontStore';
import type { SoundStore } from '../core/store/SoundStore';
import type { TextureStore } from '../core/store/TextureStore';
import { gameClock } from '../core/time';
import {
@@ -151,6 +152,7 @@ export class Editor implements PlayHost {
store: ProjectStore,
textures: TextureStore,
fonts: FontStore,
sounds: SoundStore,
) {
this.viewport = new SceneViewport(state);
this.gameView = new GameViewport(state);
@@ -196,9 +198,9 @@ export class Editor implements PlayHost {
this.workspace = new HStack();
this.leftColumn = new VStack({ gap: 1 });
this.assets = new AssetPanel(state, textures, fonts);
this.assets = new AssetPanel(state, textures, fonts, sounds);
this.leftColumn.add(this.assets, 1);
installFileImport(app.canvas, { state, textures, fonts });
installFileImport(app.canvas, { state, textures, fonts, sounds });
this.leftColumn.add(this.hierarchy, 1);
this.centre = new VStack();