音频模块
目录
我们接上一篇【资源模块】,现在继续使用AI帮我们写一个【音频管理器】。
前言
音频管理器,主要提供音乐和音效的播放、暂停、复位功能。
我们对ccc现有的音频以及音效的播放、暂停、复位功能进行了简单封装,配合【资源管理器】使用。
小白必读:音频管理器的用处?
答:音频播放是游戏中一个常用的功能,背景长音乐的循环播放;游戏中各种音频特效单次播放,如:攻击、受伤、死亡、技能释放等等。有些复杂场景下可能还需要用到音效池,提供资源的复用性,在这里我们只封装基础的音频功能。
一、新建AudioMgr.ts
我们继续在Core分包目录下的Scripts\Managers目录新建AudioMgr.ts
二、提问AI
注意:现在我们需要结合ResMgr.ts的资源加载读取,来写代码。
1、点击提问区的 + 号
2、选择ResMgr.ts
3、输入问题
结合ResMgr.ts,用ccc 3.8.x 内置 API 帮我们写一个AudioMgr.ts音频管理器,实现:
1、音乐播放、暂停、重播
2、音效播放
3、设置音乐音量
4、设置音效音量
5、加一个音效组件池,数量初始为5个,用于支持同时播放多个音效
6、参考ResMgr单例方式,并导出
三、优化修改
从上2图中我们看到有几个地方需要修改:
1、我们是独立模块,需要去掉Component继承
2、在 constructor 中 使用 director.once的Director.EVENT_AFTER_SCENE_LAUNCH事件执行init
3、在init中添加组件到场景根节点,但在编辑器模式下不执行
4、直接在新场景下添加一个__AudioMgr__的新节点,用于保存音频组件
5、播放音乐加2个参数、是否循环,音量,播放音效也加一个音量参数
6、getAvailableEffectSource中会判断音效组件是否播放状态,如果是会返回null,这样不符合我们的需求,可以采用动态增加音效池的方式解决。
四、最终版本
import { Node, AudioSource, AudioClip, director, Director, find } from 'cc';
import { resMgr } from './ResMgr';
import { EDITOR } from 'cc/env';
/**
* 音频管理器
* 用于音乐和音效的播放、暂停、重播和音量设置
*/
class AudioMgr {
private musicSource: AudioSource | null = null;
private effectSources: AudioSource[] = [];
private effectVolume: number = 1;
private musicVolume: number = 1;
private audioMgrNode: Node | null = null;
// 私有构造函数,防止外部实例化
private constructor() {
director.once(Director.EVENT_AFTER_SCENE_LAUNCH, this.init, this);
}
/** 单例实例 */
public static readonly instance: AudioMgr = new AudioMgr();
private init() {
if (EDITOR) return; // 在编辑器模式下不执行
// 初始化音效组件池并添加到场景根节点
this.audioMgrNode = new Node('__AudioMgr__');
director.getScene().addChild(this.audioMgrNode);
for (let i = 0; i < 5; i++) {
const effectSource = this.audioMgrNode.addComponent(AudioSource);
this.effectSources.push(effectSource);
}
this.musicSource = this.audioMgrNode.addComponent(AudioSource);
}
/**
* 播放音乐
* @param {string} bundleName - 分包名称
* @param {string} url - 音乐资源路径
* @param {boolean} loop - 是否循环播放
* @param {number} volume - 音量值(0-1)
*/
public playMusic(bundleName: string, url: string, loop: boolean = true, volume: number = 1) {
resMgr.loadAsset(bundleName, url, AudioClip, (err, clip) => {
if (err) {
console.error(`加载音乐失败: ${url}`, err);
return;
}
if (this.musicSource) {
this.musicSource.clip = clip;
this.musicSource.loop = loop;
this.musicSource.volume = volume * this.musicVolume;
this.musicSource.play();
}
});
}
/**
* 暂停音乐
*/
public pauseMusic() {
if (this.musicSource) {
this.musicSource.pause();
}
}
/**
* 重播音乐
*/
public resumeMusic() {
if (this.musicSource) {
this.musicSource.play();
}
}
/**
* 播放音效
* @param {string} bundleName - 分包名称
* @param {string} url - 音效资源路径
* @param {number} volume - 音量值(0-1)
*/
public playEffect(bundleName: string, url: string, volume: number = 1) {
resMgr.loadAsset(bundleName, url, AudioClip, (err, clip) => {
if (err) {
console.error(`加载音效失败: ${url}`, err);
return;
}
const effectSource = this.getAvailableEffectSource();
effectSource.clip = clip;
effectSource.volume = volume * this.effectVolume;
effectSource.play();
});
}
/**
* 设置音乐音量
* @param {number} volume - 音量值(0-1)
*/
public setMusicVolume(volume: number) {
this.musicVolume = volume;
if (this.musicSource) {
this.musicSource.volume = volume * this.musicVolume;
}
}
/**
* 设置音效音量
* @param {number} volume - 音量值(0-1)
*/
public setEffectVolume(volume: number) {
this.effectVolume = volume;
this.effectSources.forEach(source => {
source.volume = volume;
});
}
/**
* 获取可用的音效组件
* @returns {AudioSource} 可用的音效组件
*/
private getAvailableEffectSource(): AudioSource {
for (const source of this.effectSources) {
if (!source.playing) {
return source;
}
}
if (this.audioMgrNode) {
const newSource = this.audioMgrNode.addComponent(AudioSource);
this.effectSources.push(newSource);
return newSource;
} else {
throw new Error('未找到音频管理节点,无法添加新的音效组件');
}
}
}
/** 导出实例 */
export const audioMgr = AudioMgr.instance;
五、总结
由于游戏中的特效音频会有很多,我们采用音效池+动态添加的方式,可以满足日常的游戏需求。
作者:ccs2d.com 创建时间:2024-09-28 12:00
最后编辑:ccs2d.com 更新时间:2024-10-06 09:43
最后编辑:ccs2d.com 更新时间:2024-10-06 09:43