Files
tankwar_proj/docs/AudioManager_说明文档.md
T
2026-04-10 22:59:39 +08:00

8.9 KiB
Raw Blame History

AudioManager 音效系统说明文档

文件路径: js/managers/AudioManager.js
运行环境: 微信小游戏 (WeChat Mini Game)
依赖 API: wx.createWebAudioContext() (Web Audio API)
外部资源: 无 — 所有音效通过 PCM 程序化合成生成


1. 架构概述

AudioManager 是坦克大战微信小游戏的音效管理模块,采用 程序化音频合成 方案,通过 Web Audio API 在运行时生成所有游戏音效的 PCM 缓冲区,无需加载任何外部音频文件。

设计决策

方案 优点 缺点
外部音频文件 音质高、可定制 需要额外资源文件,增加包体积
程序化合成 零资源依赖、包体极小、即时可用 音效较简单,适合复古风格游戏

模块关系

game.js (初始化)
  └── AudioManager.init()  ← 创建 WebAudioContext + 预生成所有音效缓冲区
        │
        ├── GameScene.js (游戏场景)
        │     ├── _playerFire()      → playSFX('shoot')
        │     ├── _enemyFire()       → playSFX('shoot')
        │     ├── _spawnExplosion()   → playSFX('explosion_big' | 'explosion_small')
        │     ├── _checkPowerUpPickup() → playSFX('powerup')
        │     ├── _handlePlayerDestroyed() → playSFX('gameover')
        │     ├── _handleBaseDestroyed()   → playSFX('gameover')
        │     └── _checkVictory()    → playSFX('victory')
        │
        └── CollisionManager.js (碰撞管理)
              └── 子弹击中装甲坦克(未摧毁) → playSFX('hit')

2. 生命周期

sequenceDiagram
    participant G as game.js
    participant AM as AudioManager
    participant GS as GameScene

    G->>AM: new AudioManager()
    G->>G: GameGlobal.audioManager = audioManager
    G->>AM: audioManager.init()
    AM->>AM: wx.createWebAudioContext()
    AM->>AM: _generateSounds() 预生成9种音效
    Note over AM: 初始化完成,_initialized = true

    GS->>AM: playSFX('shoot')
    AM->>AM: createBufferSource() → connect → start
    Note over AM: 播放音效

    G->>AM: pauseAll() / resumeAll()
    Note over AM: 前后台切换时暂停/恢复

    G->>AM: destroy()
    AM->>AM: audioCtx.close() + buffers.clear()

3. 音效目录

3.1 完整音效列表

音效名 用途 时长 波形特征 触发位置
shoot 坦克射击 80ms 800→400Hz 下降正弦波 + 线性衰减 GameScene._playerFire() / _enemyFire()
explosion_small 小爆炸(子弹击中地形) 200ms 白噪声 + 120Hz 正弦波,二次衰减 GameScene._spawnExplosion(x, y, false)
explosion_big 大爆炸(坦克被摧毁) 400ms 白噪声 + 60Hz/90Hz 双正弦波,二次衰减 GameScene._spawnExplosion(x, y, true)
hit 子弹击中装甲(未摧毁) 100ms 1200Hz + 2400Hz 双正弦波,金属质感 CollisionManager 子弹命中装甲坦克
hit_wall 子弹击中墙壁 60ms 噪声 + 300Hz 正弦波混合 预留(当前未调用)
powerup 拾取道具 250ms 400→1200Hz 上升正弦波 + 泛音 GameScene._checkPowerUpPickup()
gameover 游戏结束 600ms 400→150Hz 下降正弦波 GameScene._handlePlayerDestroyed() / _handleBaseDestroyed()
victory 通关胜利 500ms C5→E5→G5 三音阶上升和弦 GameScene._checkVictory()
move 坦克移动 50ms 80Hz 低频正弦波 预留(当前未调用)

3.2 音效波形参数详解

shoot(射击)

时长: 0.08s
频率: 800Hz → 400Hz (线性下降)
包络: 线性衰减 (1 → 0)
振幅: 0.3

explosion_big(大爆炸)

时长: 0.4s
成分: 白噪声(50%) + 60Hz正弦(30%) + 90Hz正弦(20%)
包络: 二次衰减 (1-t)²
振幅: 0.4

victory(胜利)

时长: 0.5s
音符: C5(523Hz) → E5(659Hz) → G5(784Hz)
每段: 0.167s, 含 attack(10%) + decay(90%)
泛音: 基频 × 2, 振幅 0.15

4. API 参考

构造函数

const audioManager = new AudioManager();

创建实例,默认 soundEnabled = true, musicEnabled = true
自动监听 GameGlobal.eventBussettings:changed 事件。

init()

audioManager.init();

初始化 WebAudio 上下文并预生成所有音效缓冲区。

  • 幂等调用:多次调用只执行一次
  • 如果 wx.createWebAudioContext 不可用,静默降级(无音效)

playSFX(name)

GameGlobal.audioManager.playSFX('shoot');

播放指定名称的音效。

  • 参数: name — 音效名称,见上方音效目录
  • 如果音效未找到或音效已禁用,静默忽略
  • 每次调用创建新的 BufferSource,支持同一音效并发播放

register(name, path)

audioManager.register('custom', 'path/to/file.mp3');

向后兼容接口,当前为空操作(No-op)。

playBGM(path) / stopBGM()

背景音乐接口,当前未实现(需要外部音频文件)。

pauseAll() / resumeAll()

暂停/恢复所有音频,用于应用前后台切换。

destroy()

销毁音频上下文,释放所有缓冲区资源。

属性

属性 类型 说明
soundEnabled boolean 音效开关(getter/setter
musicEnabled boolean 音乐开关(getter/setter

5. 集成指南

5.1 初始化(game.js

// game.js 第39行
const audioManager = new AudioManager();

// 第48行 - 挂载到全局
GameGlobal.audioManager = audioManager;

// 第131行 - LoadingScene._startLoading() 中初始化
audioManager.init();

5.2 在游戏逻辑中播放音效

// 射击时
GameGlobal.audioManager.playSFX('shoot');

// 爆炸时(根据大小选择音效)
GameGlobal.audioManager.playSFX(isBig ? 'explosion_big' : 'explosion_small');

// 拾取道具
GameGlobal.audioManager.playSFX('powerup');

// 游戏结束
GameGlobal.audioManager.playSFX('gameover');

// 胜利
GameGlobal.audioManager.playSFX('victory');

5.3 添加新音效

_generateSounds() 方法中添加新的缓冲区生成:

// 示例:添加一个"警报"音效
this._buffers.set('alarm', this._generateBuffer(sampleRate, 0.3, (i, len) => {
  const t = i / len;
  const freq = 600 + Math.sin(t * 20) * 200; // 颤音效果
  const envelope = 1 - t;
  return Math.sin(2 * Math.PI * freq * i / sampleRate) * envelope * 0.3;
}));

然后在需要的地方调用:

GameGlobal.audioManager.playSFX('alarm');

5.4 前后台切换处理

// game.js 中已配置
wx.onHide(() => { audioManager.pauseAll(); });
wx.onShow(() => { audioManager.resumeAll(); });

6. 技术细节

6.1 PCM 缓冲区生成原理

每个音效通过 _generateBuffer() 方法生成:

  1. 根据 sampleRate(通常 44100Hz)和 duration 计算总采样数
  2. 创建单声道 AudioBuffer
  3. 逐采样调用 generator(sampleIndex, totalSamples) 函数
  4. 每个采样值范围 [-1.0, 1.0]
采样数 = sampleRate × duration
例: 44100 × 0.08 = 3528 个采样点 (shoot 音效)

6.2 播放机制

playSFX(name)  createBufferSource()  connect(destination)  start(0)
  • 每次播放创建新的 BufferSource 节点(Web Audio API 要求,BufferSource 是一次性的)
  • 支持同一音效的多实例并发播放(如连续射击)
  • 播放完成后 BufferSource 自动被垃圾回收

6.3 性能考量

指标 数值
预生成缓冲区数量 9 个
最大单个缓冲区大小 ~26KB (explosion_big, 0.4s × 44100 × 4bytes)
总内存占用 ~80KB
初始化耗时 < 10ms
播放延迟 < 1ms (预生成缓冲区,无需解码)

6.4 降级策略

wx.createWebAudioContext 可用?
  ├── 是 → 正常初始化,生成所有音效
  └── 否 → _initialized = false, 所有 playSFX() 静默返回

7. 已知限制与后续规划

当前限制

  1. 无背景音乐playBGM() 为空实现,需要外部音频文件支持
  2. 音效较简单 — 程序化合成适合复古风格,无法达到高保真音质
  3. hit_wallmove 音效已生成但未集成 — 预留接口,可在后续版本中启用
  4. pauseAll() / resumeAll() 为空实现 — WebAudio 的 suspend/resume 可在后续补充

后续可优化方向

  • 实现 pauseAll() / resumeAll() 使用 audioCtx.suspend() / audioCtx.resume()
  • 集成 hit_wall 音效到 CollisionManager 的墙壁碰撞逻辑
  • 集成 move 音效到坦克移动逻辑(需注意节流,避免频繁触发)
  • 添加音量控制(通过 GainNode
  • 支持外部音频文件加载,用于背景音乐
  • 音效参数可配置化(从 JSON 配置文件读取波形参数)