由于 Cocos Creator 3.x 移除了 v2.x cc.audioEngine
系列的 API,统一使用 AudioSource 控制音频播放。
根据cocos官网给的例子按自己实际使用修改的一个小工具🔧
先看官网给的例子,官方现在推荐使用的是audioSource
, 播放音乐用 play
, 播放音效用 playOneShot
。但是一个audioSource
只能同时操作单个音频,所以当我们有多个音频需要同时播放时就需要做一些“加工”了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 import { Node , AudioSource , AudioClip , resources, director } from 'cc' ; export class AudioMgr { private static _inst : AudioMgr ; public static get inst (): AudioMgr { if (this ._inst == null ) { this ._inst = new AudioMgr (); } return this ._inst ; } private _audioSource : AudioSource ; constructor ( ) { let audioMgr = new Node (); audioMgr.name = '__audioMgr__' ; director.getScene ().addChild (audioMgr); director.addPersistRootNode (audioMgr); this ._audioSource = audioMgr.addComponent (AudioSource ); } public get audioSource () { return this ._audioSource ; } playOneShot (sound: AudioClip | string , volume: number = 1.0 ) { if (sound instanceof AudioClip ) { this ._audioSource .playOneShot (sound, volume); } else { resources.load (sound, (err, clip: AudioClip ) => { if (err) { console .log (err); } else { this ._audioSource .playOneShot (clip, volume); } }); } } play (sound: AudioClip | string , volume: number = 1.0 ) { if (sound instanceof AudioClip ) { this ._audioSource .stop (); this ._audioSource .clip = sound; this ._audioSource .play (); this .audioSource .volume = volume; } else { resources.load (sound, (err, clip: AudioClip ) => { if (err) { console .log (err); } else { this ._audioSource .stop (); this ._audioSource .clip = clip; this ._audioSource .play (); this .audioSource .volume = volume; } }); } } stop ( ) { this ._audioSource .stop (); } pause ( ) { this ._audioSource .pause (); } resume ( ){ this ._audioSource .play (); } }
那么我们需要做哪些改动呢?其实也很简单,就是一个音频对应一个audioSource
,这里我用Map
存储了音效组件AudioSource
。
1 2 3 4 private effectList : Map <string , AudioSource > = new Map ();
接下来在播放音频时,如果是第一次播放那就创建一个节点用来控制该音频,同时添加AudioSource
组件到节点,然后把加载好的音频Clip
绑定到AudioSource
组件上,最后就是往effectList
里set
新创建的音频组件。这样一顿操作,第二次播放时就可以直接复用已经存在的组件了,节省了系统开销。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 async playEffect (sound: string , volume: number = 1.0 ) { let audioSource : AudioSource | undefined = this .effectList .get (sound); if (!audioSource) { const audioClip = await BundleManager .get ().loadAudio (sound); if (!audioClip) return ; const audioNode = new Node (); audioNode.name = sound; audioNode.parent = this .audioMgr ; audioSource = audioNode.addComponent (AudioSource ); audioSource.name = sound; audioSource.clip = audioClip; audioSource.volume = volume; this .effectList .set (sound, audioSource); } if (audioSource.clip ) { audioSource.playOneShot (audioSource.clip ); } }
那么长音频该怎么处理呢?
我这里以背景音乐举例,首先定义一个属性😂
1 2 3 4 private bgMusic : AudioSource
很简单,直接看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 async playMusic (sound: string , loop: boolean = true , volume: number = 1.0 ) { if (!this .bgMusic ) { const audioClip = await BundleManager .get ().loadAudio (sound); if (!audioClip) return ; const audioNode = new Node (); audioNode.name = sound; audioNode.parent = this .audioMgr ; const audioSource = audioNode.addComponent (AudioSource ); audioSource.name = sound; audioSource.clip = audioClip; audioSource.volume = volume; audioSource.loop = loop; this .bgMusic = audioSource } if (this .bgMusic .clip ) { this .bgMusic .play (); } }
最后完整代码奉上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 import { Node , AudioSource , director } from 'cc' ;import BundleManager from '../bundle/BundleManager' ;export class AudioManager { private static _inst : AudioManager ; public static get inst (): AudioManager { if (this ._inst == null ) { this ._inst = new AudioManager (); } return this ._inst ; } private audioMgr : Node ; private effectList : Map <string , AudioSource > = new Map (); private bgMusic : AudioSource constructor ( ) { this .audioMgr = new Node (); this .audioMgr .name = '__audioMgr__' ; director.getScene ().addChild (this .audioMgr ); director.addPersistRootNode (this .audioMgr ); } async playEffect (sound: string , volume: number = 1.0 ) { let audioSource : AudioSource | undefined = this .effectList .get (sound); if (!audioSource) { const audioClip = await BundleManager .get ().loadAudio (sound); if (!audioClip) return ; const audioNode = new Node (); audioNode.name = sound; audioNode.parent = this .audioMgr ; audioSource = audioNode.addComponent (AudioSource ); audioSource.name = sound; audioSource.clip = audioClip; audioSource.volume = volume; this .effectList .set (sound, audioSource); } if (audioSource.clip ) { audioSource.play (); } } async playMusic (sound: string , loop: boolean = true , volume: number = 1.0 ) { if (!this .bgMusic ) { const audioClip = await BundleManager .get ().loadAudio (sound); if (!audioClip) return ; const audioNode = new Node (); audioNode.name = sound; audioNode.parent = this .audioMgr ; const audioSource = audioNode.addComponent (AudioSource ); audioSource.name = sound; audioSource.clip = audioClip; audioSource.volume = volume; audioSource.loop = loop; this .bgMusic = audioSource } if (this .bgMusic .clip ) { this .bgMusic .play (); } } public stopMusic (): void { if (!this .bgMusic || !this .bgMusic .playing ) return ; this .bgMusic .stop (); } public stopEffect (sound : string ): void { let audioSource : AudioSource | undefined = this .effectList .get (sound); if (!audioSource || !audioSource.playing ) return ; audioSource.stop (); } }