SoundManager.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. const {ccclass, property} = cc._decorator;
  2. @ccclass
  3. export class SoundManager extends cc.Component {
  4. /**
  5. * 背景音乐通道
  6. */
  7. private musicSoundChannel:AudioChannel;
  8. /**
  9. * 固定通道
  10. */
  11. private soundChannelMap:Map<string,AudioChannel>;
  12. /**
  13. * 重复利用通道
  14. */
  15. private recycleSoundChannelList:AudioChannel[];
  16. private recycleSoundChannelPool:AudioChannel[];
  17. onLoad():void{
  18. SoundManager.instance=this;
  19. this.soundChannelMap=new Map<string,AudioChannel>();
  20. this.recycleSoundChannelList=[];
  21. this.recycleSoundChannelPool=[];
  22. }
  23. start () {
  24. //背景音乐通道
  25. this.musicSoundChannel=new AudioChannel();
  26. this.musicSoundChannel.aSComponent=this.addComponent(cc.AudioSource);
  27. //最大重复利用通道4个
  28. let channel:AudioChannel;
  29. for (let index = 0; index < 6; index++) {
  30. channel=new AudioChannel();
  31. channel.aSComponent=this.addComponent(cc.AudioSource);
  32. this.recycleSoundChannelPool.push(channel);
  33. }
  34. }
  35. /**
  36. * 播放音频
  37. * @param assetBundle
  38. * @param soundPath
  39. */
  40. public PlayMusic(assetBundle:string,soundPath:string):void{
  41. this.musicSoundChannel.Play(assetBundle,soundPath,true);
  42. }
  43. /**
  44. * 固定通道播放音效
  45. * @param channel 通道
  46. * @param assetBundle 资源包
  47. * @param soundPath 音效地址
  48. * @param isLoop 是否循环播放
  49. * @param force 是否在相同音频的情况下强制从头开始播放
  50. */
  51. public PlaySound(channel:string,assetBundle:string,soundPath:string,isLoop:boolean=false,force:boolean=false):void{
  52. let audioChannel:AudioChannel;
  53. if(this.soundChannelMap.has(channel)==false){
  54. audioChannel=new AudioChannel();
  55. audioChannel.aSComponent=this.addComponent(cc.AudioSource);
  56. this.soundChannelMap.set(channel,audioChannel);
  57. }else{
  58. audioChannel=this.soundChannelMap.get(channel);
  59. }
  60. audioChannel.Play(assetBundle,soundPath,isLoop,force);
  61. }
  62. /**
  63. * 清理通道
  64. * @param channel
  65. */
  66. public ClearSound(channel:string):void{
  67. if(this.soundChannelMap.has(channel)==false){
  68. return;
  69. }
  70. let audioChannel:AudioChannel=this.soundChannelMap.get(channel);
  71. audioChannel.Dispose();
  72. this.soundChannelMap.delete(channel);
  73. }
  74. /**
  75. * 停止某个固定通道的音效
  76. * @param channel
  77. */
  78. public StopSound(channel:string):void{
  79. if(this.soundChannelMap.has(channel)==false){
  80. return;
  81. }
  82. let audioChannel:AudioChannel=this.soundChannelMap.get(channel);
  83. audioChannel.Stop();
  84. }
  85. /**
  86. * 某个固定频道是否在播放中
  87. * @param channel
  88. */
  89. public ChannelIsPlaying(channel:string):boolean{
  90. if(this.soundChannelMap.has(channel)==false){
  91. return false;
  92. }
  93. let audioChannel:AudioChannel=this.soundChannelMap.get(channel);
  94. return audioChannel.isPlaying;
  95. }
  96. /**
  97. * 通道播放音效
  98. * @param assetBundle 资源包
  99. * @param soundPath 音效地址
  100. */
  101. public PlaySoundRecycleChannel(assetBundle:string,soundPath:string):void{
  102. let audioChannel:AudioChannel;
  103. //检测是否有已经播放完成的通道
  104. for (let index = 0; index < this.recycleSoundChannelList.length; index++) {
  105. const element = this.recycleSoundChannelList[index];
  106. if(element.isPlaying==false){
  107. this.recycleSoundChannelPool.push(element);
  108. this.recycleSoundChannelList.splice(index,1);
  109. }
  110. }
  111. //如果池中没有,则不播放该音效!
  112. if(this.recycleSoundChannelPool.length==0){
  113. return;
  114. }
  115. audioChannel=this.recycleSoundChannelPool.pop();
  116. this.recycleSoundChannelList.push(audioChannel);
  117. //标记为被占有
  118. audioChannel.Play(assetBundle,soundPath,false);
  119. }
  120. // update (deltaTime: number) {
  121. // // Your update function goes here.
  122. // }
  123. private static instance:SoundManager;
  124. public static get single():SoundManager{
  125. return this.instance;
  126. }
  127. }
  128. class AudioChannel{
  129. /**
  130. * 音频资源
  131. */
  132. private static audioClipMap:Map<string,cc.AudioClip>=new Map<string,cc.AudioClip>();
  133. aSComponent:cc.AudioSource;
  134. private assetBundleName:string;
  135. private soundUrl:string;
  136. private isLoop:boolean;
  137. private force:boolean;
  138. /**
  139. * 0 空闲 1加载中 2播放中
  140. */
  141. private state:number=0;
  142. constructor(){
  143. }
  144. Play(assetBundleName:string,soundUrl:string,isLoop:boolean,force:boolean=false):void{
  145. if(this.assetBundleName==assetBundleName&&this.soundUrl==soundUrl&&this.state==1){
  146. return;
  147. }
  148. this.assetBundleName=assetBundleName;
  149. this.soundUrl=soundUrl;
  150. this.isLoop=isLoop;
  151. this.force=force;
  152. this.state=1;
  153. if(AudioChannel.audioClipMap.has(this.soundUrl)){
  154. let clip:cc.AudioClip=AudioChannel.audioClipMap.get(this.soundUrl);
  155. console.log("播放音频缓存!");
  156. this.__play(clip);
  157. }else{
  158. if(this.assetBundle==null){
  159. cc.assetManager.loadBundle(this.assetBundleName,this.OnAssetBundleLoadComplete.bind(this));
  160. }else{
  161. this.LoadAudioClip();
  162. }
  163. }
  164. }
  165. private OnAssetBundleLoadComplete(err:Error,bundle:cc.AssetManager.Bundle):void{
  166. if(err){
  167. throw new Error("加载AssetBundle:"+this.assetBundleName+"出错!");
  168. }
  169. this.LoadAudioClip();
  170. }
  171. private LoadAudioClip():void{
  172. this.assetBundle.load(this.soundUrl,cc.AudioClip,this.OnAudioLoadComplete.bind(this));
  173. }
  174. private OnAudioLoadComplete(err:Error,asset:cc.AudioClip):void{
  175. if(err){
  176. console.error("加载音乐错误!");
  177. return;
  178. }
  179. AudioChannel.audioClipMap.set(this.soundUrl,asset);
  180. if(this.state==0){
  181. return;
  182. }
  183. this.__play(asset);
  184. }
  185. private __play(audioClip:cc.AudioClip):void{
  186. this.state=2;
  187. if(this.aSComponent==null){
  188. return;
  189. }
  190. if(this.aSComponent.clip!=audioClip){
  191. this.aSComponent.stop();
  192. this.aSComponent.clip=audioClip;
  193. }
  194. this.aSComponent.loop=this.isLoop;
  195. if(this.force){
  196. this.aSComponent.setCurrentTime(0);
  197. }
  198. this.aSComponent.play();
  199. }
  200. Stop():void{
  201. this.state=0;
  202. this.soundUrl=null;
  203. this.aSComponent.stop();
  204. }
  205. Dispose():void{
  206. this.soundUrl=null;
  207. this.aSComponent.destroy();
  208. this.aSComponent=null;
  209. }
  210. get isPlaying():boolean{
  211. return this.state>0;
  212. }
  213. private get assetBundle():cc.AssetManager.Bundle{
  214. return cc.assetManager.getBundle(this.assetBundleName);
  215. }
  216. }