2026-02-11 19:40:26 +08:00
|
|
|
|
# 08. 音频系统
|
|
|
|
|
|
|
|
|
|
|
|
Extra2D 提供了基于 SDL2_mixer 的音频播放系统,支持音效播放。
|
|
|
|
|
|
|
|
|
|
|
|
## 音频引擎
|
|
|
|
|
|
|
|
|
|
|
|
通过 `Application::instance().audio()` 访问音频引擎:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
auto& audio = Application::instance().audio();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 播放音效
|
|
|
|
|
|
|
|
|
|
|
|
### 基本用法
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// 加载音效
|
|
|
|
|
|
auto sound = audio.loadSound("assets/audio/jump.wav");
|
|
|
|
|
|
|
|
|
|
|
|
// 播放音效
|
|
|
|
|
|
sound->play();
|
|
|
|
|
|
|
|
|
|
|
|
// 设置音量 (0.0 - 1.0)
|
|
|
|
|
|
sound->setVolume(0.8f);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 音效控制
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// 停止播放
|
|
|
|
|
|
sound->stop();
|
|
|
|
|
|
|
|
|
|
|
|
// 暂停
|
|
|
|
|
|
sound->pause();
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复
|
|
|
|
|
|
sound->resume();
|
|
|
|
|
|
|
|
|
|
|
|
// 循环播放
|
|
|
|
|
|
sound->setLooping(true);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查播放状态
|
|
|
|
|
|
bool playing = sound->isPlaying();
|
|
|
|
|
|
bool paused = sound->isPaused();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 音调和播放位置
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// 设置音调(当前实现不支持)
|
|
|
|
|
|
sound->setPitch(1.0f);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取/设置播放位置(当前实现不支持)
|
|
|
|
|
|
float cursor = sound->getCursor();
|
|
|
|
|
|
sound->setCursor(0.0f);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取音频时长(当前实现不支持)
|
|
|
|
|
|
float duration = sound->getDuration();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 全局音量控制
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// 设置主音量
|
|
|
|
|
|
audio.setMasterVolume(0.8f);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取主音量
|
|
|
|
|
|
float volume = audio.getMasterVolume();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 全局播放控制
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// 暂停所有音效
|
|
|
|
|
|
audio.pauseAll();
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复所有音效
|
|
|
|
|
|
audio.resumeAll();
|
|
|
|
|
|
|
|
|
|
|
|
// 停止所有音效
|
|
|
|
|
|
audio.stopAll();
|
|
|
|
|
|
|
|
|
|
|
|
// 卸载指定音效
|
|
|
|
|
|
audio.unloadSound("jump");
|
|
|
|
|
|
|
|
|
|
|
|
// 卸载所有音效
|
|
|
|
|
|
audio.unloadAllSounds();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 完整示例
|
|
|
|
|
|
|
2026-02-13 08:56:27 +08:00
|
|
|
|
### Flappy Bird 音效管理器
|
|
|
|
|
|
|
|
|
|
|
|
参考 `examples/flappy_bird/ResLoader.h/cpp`:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// ResLoader.h
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
|
|
#include <extra2d/extra2d.h>
|
|
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
|
|
|
|
namespace flappybird {
|
|
|
|
|
|
|
|
|
|
|
|
enum class MusicType {
|
|
|
|
|
|
Click, // 按键声音
|
|
|
|
|
|
Hit, // 小鸟死亡声音
|
|
|
|
|
|
Fly, // 小鸟飞翔声音
|
|
|
|
|
|
Point, // 得分声音
|
|
|
|
|
|
Swoosh // 转场声音
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class ResLoader {
|
|
|
|
|
|
public:
|
|
|
|
|
|
static void init();
|
|
|
|
|
|
static void playMusic(MusicType type);
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
static std::map<MusicType, extra2d::Ptr<extra2d::Sound>> soundMap_;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace flappybird
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// ResLoader.cpp
|
|
|
|
|
|
#include "ResLoader.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace flappybird {
|
|
|
|
|
|
|
|
|
|
|
|
std::map<MusicType, extra2d::Ptr<extra2d::Sound>> ResLoader::soundMap_;
|
|
|
|
|
|
|
|
|
|
|
|
void ResLoader::init() {
|
|
|
|
|
|
auto &resources = extra2d::Application::instance().resources();
|
|
|
|
|
|
|
|
|
|
|
|
// 加载所有音效
|
|
|
|
|
|
soundMap_[MusicType::Click] = resources.loadSound("assets/sound/click.wav");
|
|
|
|
|
|
soundMap_[MusicType::Hit] = resources.loadSound("assets/sound/hit.wav");
|
|
|
|
|
|
soundMap_[MusicType::Fly] = resources.loadSound("assets/sound/fly.wav");
|
|
|
|
|
|
soundMap_[MusicType::Point] = resources.loadSound("assets/sound/point.wav");
|
|
|
|
|
|
soundMap_[MusicType::Swoosh] = resources.loadSound("assets/sound/swoosh.wav");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ResLoader::playMusic(MusicType type) {
|
|
|
|
|
|
auto it = soundMap_.find(type);
|
|
|
|
|
|
if (it != soundMap_.end() && it->second) {
|
|
|
|
|
|
it->second->play();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace flappybird
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 在游戏中使用
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// GameScene.cpp - 得分时播放音效
|
|
|
|
|
|
if (pipeX <= birdX) {
|
|
|
|
|
|
score_++;
|
|
|
|
|
|
scoreNumber_->setNumber(score_);
|
|
|
|
|
|
firstPipe->scored = true;
|
|
|
|
|
|
ResLoader::playMusic(MusicType::Point); // 播放得分音效
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// bird.cpp - 跳跃时播放音效
|
|
|
|
|
|
void Bird::jump() {
|
|
|
|
|
|
velocity_.y = jumpForce_;
|
|
|
|
|
|
ResLoader::playMusic(MusicType::Fly); // 播放飞翔音效
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GameOverLayer.cpp - 按钮点击时播放音效
|
|
|
|
|
|
restartBtn_->setOnClick([]() {
|
|
|
|
|
|
ResLoader::playMusic(MusicType::Click); // 播放点击音效
|
|
|
|
|
|
auto &app = extra2d::Application::instance();
|
2026-02-23 22:56:31 +08:00
|
|
|
|
app.scenes().replaceScene(extra2d::shared<GameScene>(),
|
2026-02-13 08:56:27 +08:00
|
|
|
|
extra2d::TransitionType::Fade, 0.5f);
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 推箱子音效管理器
|
|
|
|
|
|
|
2026-02-11 19:40:26 +08:00
|
|
|
|
```cpp
|
|
|
|
|
|
// audio_manager.h
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
|
|
#include <extra2d/extra2d.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace pushbox {
|
|
|
|
|
|
|
|
|
|
|
|
class AudioManager {
|
|
|
|
|
|
public:
|
|
|
|
|
|
static AudioManager& instance() {
|
|
|
|
|
|
static AudioManager instance;
|
|
|
|
|
|
return instance;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void init();
|
|
|
|
|
|
void setEnabled(bool enabled);
|
|
|
|
|
|
void playMoveSound();
|
|
|
|
|
|
void playBoxMoveSound();
|
|
|
|
|
|
void playWinSound();
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
AudioManager() = default;
|
|
|
|
|
|
|
|
|
|
|
|
bool enabled_ = true;
|
|
|
|
|
|
extra2d::Ptr<extra2d::Sound> moveSound_;
|
|
|
|
|
|
extra2d::Ptr<extra2d::Sound> boxMoveSound_;
|
|
|
|
|
|
extra2d::Ptr<extra2d::Sound> winSound_;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace pushbox
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// audio_manager.cpp
|
|
|
|
|
|
#include "audio_manager.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace pushbox {
|
|
|
|
|
|
|
|
|
|
|
|
void AudioManager::init() {
|
|
|
|
|
|
auto& audio = extra2d::Application::instance().audio();
|
|
|
|
|
|
|
|
|
|
|
|
// 加载音效
|
|
|
|
|
|
moveSound_ = audio.loadSound("move", "assets/audio/manmove.wav");
|
|
|
|
|
|
boxMoveSound_ = audio.loadSound("boxmove", "assets/audio/boxmove.wav");
|
|
|
|
|
|
winSound_ = audio.loadSound("win", "assets/audio/win.wav");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AudioManager::setEnabled(bool enabled) {
|
|
|
|
|
|
enabled_ = enabled;
|
|
|
|
|
|
if (!enabled) {
|
|
|
|
|
|
extra2d::Application::instance().audio().stopAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AudioManager::playMoveSound() {
|
|
|
|
|
|
if (enabled_ && moveSound_) {
|
|
|
|
|
|
moveSound_->play();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AudioManager::playBoxMoveSound() {
|
|
|
|
|
|
if (enabled_ && boxMoveSound_) {
|
|
|
|
|
|
boxMoveSound_->play();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AudioManager::playWinSound() {
|
|
|
|
|
|
if (enabled_ && winSound_) {
|
|
|
|
|
|
winSound_->play();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace pushbox
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 使用音频管理器
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// main.cpp
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
|
// ... 初始化应用 ...
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化音频管理器
|
|
|
|
|
|
pushbox::AudioManager::instance().init();
|
|
|
|
|
|
|
|
|
|
|
|
// ... 运行应用 ...
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PlayScene.cpp
|
|
|
|
|
|
void PlayScene::move(int dx, int dy, int direct) {
|
|
|
|
|
|
// ... 移动逻辑 ...
|
|
|
|
|
|
|
|
|
|
|
|
if (isBoxMoved) {
|
|
|
|
|
|
// 播放推箱子音效
|
|
|
|
|
|
pushbox::AudioManager::instance().playBoxMoveSound();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 播放移动音效
|
|
|
|
|
|
pushbox::AudioManager::instance().playMoveSound();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 音频开关控制
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// 在菜单中切换音效
|
|
|
|
|
|
void StartScene::onUpdate(float dt) {
|
|
|
|
|
|
auto& input = Application::instance().input();
|
|
|
|
|
|
|
|
|
|
|
|
// X键切换音效
|
|
|
|
|
|
if (input.isButtonPressed(GamepadButton::X)) {
|
|
|
|
|
|
g_SoundOpen = !g_SoundOpen;
|
|
|
|
|
|
AudioManager::instance().setEnabled(g_SoundOpen);
|
|
|
|
|
|
updateSoundIcon();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 支持的音频格式
|
|
|
|
|
|
|
|
|
|
|
|
- WAV
|
|
|
|
|
|
- OGG
|
|
|
|
|
|
- MP3(需要 SDL2_mixer 支持)
|
|
|
|
|
|
|
|
|
|
|
|
## 最佳实践
|
|
|
|
|
|
|
|
|
|
|
|
1. **使用单例管理器** - 集中管理音频资源
|
|
|
|
|
|
2. **预加载常用音效** - 在初始化时加载
|
|
|
|
|
|
3. **提供开关选项** - 让用户控制音效
|
|
|
|
|
|
4. **合理设置音量** - 避免音量过大
|
|
|
|
|
|
5. **及时卸载不用的音效** - 释放内存资源
|
|
|
|
|
|
|
|
|
|
|
|
## API 参考
|
|
|
|
|
|
|
|
|
|
|
|
### Sound 类
|
|
|
|
|
|
|
|
|
|
|
|
| 方法 | 说明 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| `play()` | 播放音效 |
|
|
|
|
|
|
| `pause()` | 暂停播放 |
|
|
|
|
|
|
| `resume()` | 恢复播放 |
|
|
|
|
|
|
| `stop()` | 停止播放 |
|
|
|
|
|
|
| `isPlaying()` | 是否正在播放 |
|
|
|
|
|
|
| `isPaused()` | 是否已暂停 |
|
|
|
|
|
|
| `setVolume(float)` | 设置音量 (0.0-1.0) |
|
|
|
|
|
|
| `getVolume()` | 获取音量 |
|
|
|
|
|
|
| `setLooping(bool)` | 设置循环播放 |
|
|
|
|
|
|
| `isLooping()` | 是否循环播放 |
|
|
|
|
|
|
| `setPitch(float)` | 设置音调(当前不支持) |
|
|
|
|
|
|
| `getPitch()` | 获取音调 |
|
|
|
|
|
|
| `getDuration()` | 获取时长(当前不支持) |
|
|
|
|
|
|
| `getCursor()` | 获取播放位置(当前不支持) |
|
|
|
|
|
|
| `setCursor(float)` | 设置播放位置(当前不支持) |
|
|
|
|
|
|
|
|
|
|
|
|
### AudioEngine 类
|
|
|
|
|
|
|
|
|
|
|
|
| 方法 | 说明 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| `getInstance()` | 获取单例实例 |
|
|
|
|
|
|
| `initialize()` | 初始化音频引擎 |
|
|
|
|
|
|
| `shutdown()` | 关闭音频引擎 |
|
|
|
|
|
|
| `loadSound(path)` | 加载音效(以路径为名称) |
|
|
|
|
|
|
| `loadSound(name, path)` | 加载音效(指定名称) |
|
|
|
|
|
|
| `getSound(name)` | 获取已加载的音效 |
|
|
|
|
|
|
| `unloadSound(name)` | 卸载指定音效 |
|
|
|
|
|
|
| `unloadAllSounds()` | 卸载所有音效 |
|
|
|
|
|
|
| `setMasterVolume(float)` | 设置主音量 |
|
|
|
|
|
|
| `getMasterVolume()` | 获取主音量 |
|
|
|
|
|
|
| `pauseAll()` | 暂停所有音效 |
|
|
|
|
|
|
| `resumeAll()` | 恢复所有音效 |
|
|
|
|
|
|
| `stopAll()` | 停止所有音效 |
|
|
|
|
|
|
|
|
|
|
|
|
## 总结
|
|
|
|
|
|
|
|
|
|
|
|
至此,你已经学习了 Extra2D 引擎的核心功能:
|
|
|
|
|
|
|
|
|
|
|
|
1. [快速开始](./01_Quick_Start.md) - 引擎基础
|
|
|
|
|
|
2. [场景系统](./02_Scene_System.md) - 场景管理
|
|
|
|
|
|
3. [节点系统](./03_Node_System.md) - 游戏对象
|
|
|
|
|
|
4. [资源管理](./04_Resource_Management.md) - 资源加载
|
|
|
|
|
|
5. [输入处理](./05_Input_Handling.md) - 输入控制
|
|
|
|
|
|
6. [碰撞检测](./06_Collision_Detection.md) - 空间索引
|
|
|
|
|
|
7. [UI 系统](./07_UI_System.md) - 界面控件
|
|
|
|
|
|
8. [音频系统](./08_Audio_System.md) - 音频播放
|
|
|
|
|
|
|
|
|
|
|
|
开始你的游戏开发之旅吧!
|