@combos-fun/plugin-sound 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,27 @@
1
1
  # @combos-fun/plugin-sound
2
2
 
3
- Internal workspace package (Combos Fun monorepo).
3
+ @combos-fun/plugin-sound part of the Combos Fun engine monorepo.
4
+
5
+ Keywords: `sound`, `audio`, `playback`, `volume`, `web-audio`, `sfx`, `bgm`.
6
+
7
+ ## Documentation
8
+
9
+ - Per-package agent / developer notes: [`agent-skill.md`](./agent-skill.md)
10
+ - Machine-readable manifest: [`combos-plugin.json`](./combos-plugin.json)
11
+ (validated against `schemas/combos-plugin.schema.json` in the repo
12
+ root)
13
+ - Entry skill (single source of truth for AI agents working with this
14
+ monorepo): `skills/combos-engine-development/SKILL.md`
15
+
16
+ When consumed from npm, the manifest and agent notes are exposed as
17
+ stable subpaths:
18
+
19
+ ```ts
20
+ import manifest from '@combos-fun/plugin-sound/plugin-manifest';
21
+ // or fetch the markdown directly:
22
+ // require.resolve('@combos-fun/plugin-sound/agent-skill')
23
+ ```
24
+
25
+ ## License
26
+
27
+ Internal workspace package, part of the Combos Fun engine monorepo.
package/agent-skill.md ADDED
@@ -0,0 +1,111 @@
1
+ # `@combos-fun/plugin-sound` — Agent notes
2
+
3
+ Audio playback for Combos Fun. Web Audio internally (`AudioContext` + `AudioBufferSourceNode` + `GainNode`). Shared between 2D and 3D — there is no spatial / 3D-positional audio in this package.
4
+
5
+ ## When to read
6
+
7
+ Read for any audio task: SFX, BGM, autoplay, loop, mute, volume, pause / resume integration with `Game.pause()` / `Game.resume()`, touch-to-unlock on mobile.
8
+
9
+ ## Public API
10
+
11
+ ```ts
12
+ import { Sound, SoundSystem } from '@combos-fun/plugin-sound';
13
+ import { resource, RESOURCE_TYPE } from '@combos-fun/engine';
14
+ ```
15
+
16
+ ### `Sound` Component params
17
+
18
+ | Field | Type | Notes |
19
+ |-------|------|-------|
20
+ | `resource` | `string` | Engine resource name registered with `RESOURCE_TYPE.AUDIO` |
21
+ | `autoplay` / `loop` / `muted` | `boolean?` | |
22
+ | `volume` | `number?` | `0` – `1` |
23
+ | `seek` / `duration` | `number?` | |
24
+ | `onEnd` | `() => void?` | Fires when playback ends naturally |
25
+
26
+ Methods: `play()`, `pause()`, `stop()`. Read-only fields: `playing`, `state` (`'unloaded' | 'loading' | 'loaded'`), `muted`, `volume`.
27
+
28
+ ### `SoundSystemParams`
29
+
30
+ | Field | Type | Notes |
31
+ |-------|------|-------|
32
+ | `onError` | `(error: unknown) => void` | **Required** — TypeScript will fail without it |
33
+ | `autoPauseAndStart` | `boolean?` | Pause / resume audio along with `Game` |
34
+
35
+ System API: `resumeAll()`, `pauseAll()`, `stopAll()`, `muted`, `volume`, `audioLocked` (true until first user gesture). Decoded `AudioBuffer`s are cached per resource.
36
+
37
+ ### Audio resource registration
38
+
39
+ ```ts
40
+ resource.addResource([
41
+ { name: 'bgm', src: { audio: { type: 'audio', url: '/bgm.mp3' } }, preload: true },
42
+ ]);
43
+ ```
44
+
45
+ The loader configures `mp3` / `wav` / `aac` / `ogg` as `arrayBuffer`. The audio buffer is decoded once and reused.
46
+
47
+ ## Required setup
48
+
49
+ - Add `SoundSystem` after the renderer system. Order does not affect
50
+ audio correctness, but keeping it consistent helps debugging.
51
+ - Always pass `onError` — this is enforced by TypeScript.
52
+ - Audio is locked until the first user gesture (browser autoplay policy).
53
+ Either start audio inside an event-driven Component (e.g. on tap) or
54
+ show a "tap to start" UI on first paint.
55
+
56
+ ## Runtime behaviour
57
+
58
+ - `Sound.play()` resolves to a fresh `AudioBufferSourceNode` per call;
59
+ re-`play()` after `stop()` works.
60
+ - `SoundSystem` listens to the unlock gesture once and sets
61
+ `audioLocked = false`. Subsequent plays succeed without user gesture.
62
+ - `autoPauseAndStart: true` calls `pauseAll()` on `Game.pause()` and
63
+ `resumeAll()` on `Game.resume()`.
64
+
65
+ ## Common pitfalls
66
+
67
+ | Symptom | Fix |
68
+ |---------|-----|
69
+ | TS: `onError` missing | `new SoundSystem({ onError: (e) => console.warn(e) })` |
70
+ | No sound on page load | Browser autoplay policy — call `play()` from a user-gesture Component |
71
+ | `state === 'unloaded'` | Resource not registered, or component `resource` name doesn't match |
72
+ | `state === 'loading'` forever | Resource URL 404 or CORS — check network tab |
73
+ | Stuck loud after pause | `volume` is per Sound; use `SoundSystem.volume` for global mute |
74
+
75
+ ## Minimal example
76
+
77
+ ```ts
78
+ import { Game, GameObject, resource } from '@combos-fun/engine';
79
+ import { RendererSystem } from '@combos-fun/plugin-renderer';
80
+ import { Sound, SoundSystem } from '@combos-fun/plugin-sound';
81
+
82
+ resource.addResource([
83
+ { name: 'tap-sfx', src: { audio: { type: 'audio', url: '/tap.mp3' } }, preload: true },
84
+ ]);
85
+
86
+ new Game({
87
+ systems: [
88
+ new RendererSystem({ canvas, width: 750, height: 1334 }),
89
+ new SoundSystem({ onError: (e) => console.warn(e) }),
90
+ ],
91
+ onSystemsBootstrapComplete: (game) => {
92
+ const audio = new GameObject('tap-audio');
93
+ audio.addComponent(new Sound({ resource: 'tap-sfx' }));
94
+ game.getScene().addChild(audio);
95
+
96
+ // Trigger from a tap Component:
97
+ // gameObject.getComponent(Sound)?.play()
98
+ },
99
+ });
100
+ ```
101
+
102
+ ## Video
103
+
104
+ `RESOURCE_TYPE.VIDEO` is supported by `@combos-fun/engine`'s loader for `{ video: { type: 'mp4', url: '...' } }`, but on-canvas video playback is app- or plugin-specific. After `getResource(name)`, use the resolved `instance` in your video plugin or a DOM `<video>` element.
105
+
106
+ ## Verification
107
+
108
+ - `pnpm --filter @combos-fun/plugin-sound run build`
109
+ - Run an example with audio on touch / click: verify autoplay-locked state
110
+ unlocks after the first user gesture and the buffer is cached for
111
+ subsequent plays.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@combos-fun/plugin-sound",
3
+ "pluginId": "sound",
4
+ "category": "audio",
5
+ "dimension": "shared",
6
+ "isCore": false,
7
+ "keywords": ["sound", "audio", "playback", "volume", "web-audio", "sfx", "bgm"],
8
+ "agentSkill": "./agent-skill.md",
9
+ "requires": ["@combos-fun/engine"],
10
+ "exports": ["Sound", "SoundSystem"]
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@combos-fun/plugin-sound",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "@combos-fun/plugin-sound",
5
5
  "main": "index.js",
6
6
  "module": "dist/plugin-sound.esm.js",
@@ -8,8 +8,22 @@
8
8
  "unpkg": "dist/CombosFun.plugin.sound.min.js",
9
9
  "files": [
10
10
  "index.js",
11
- "dist"
11
+ "dist",
12
+ "agent-skill.md",
13
+ "combos-plugin.json"
12
14
  ],
15
+ "exports": {
16
+ ".": {
17
+ "import": "./dist/plugin-sound.esm.js",
18
+ "require": "./index.js",
19
+ "types": "./dist/plugin-sound.d.ts"
20
+ },
21
+ "./plugin-manifest": "./combos-plugin.json",
22
+ "./agent-skill": "./agent-skill.md"
23
+ },
24
+ "combos": {
25
+ "pluginManifest": "./combos-plugin.json"
26
+ },
13
27
  "types": "dist/plugin-sound.d.ts",
14
28
  "keywords": [
15
29
  "combos-fun",
@@ -18,7 +32,7 @@
18
32
  "author": "sun668 <q947692259@gmail.com>",
19
33
  "dependencies": {
20
34
  "eventemitter3": "^5.0.4",
21
- "@combos-fun/engine": "0.0.6"
35
+ "@combos-fun/engine": "0.0.8"
22
36
  },
23
37
  "scripts": {
24
38
  "build": "node ../../scripts/build-package.mjs"