audioManager.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import { AssetManager, assetManager, AudioClip, AudioSource} from "cc";
  2. import { constants } from "../data/constants";
  3. import settingData from "../data/settingData";
  4. /**音乐播放数据结构*/
  5. export interface IAudioData {
  6. clip: AudioClip;//音乐片段
  7. audioSource: AudioSource;//音乐具柄
  8. path: string //音乐名称路径
  9. playStatus: boolean //播放状态
  10. totalTime: number;//音乐总长度时间
  11. time_id: number;//定时器句柄
  12. t: number;//统计记时
  13. }
  14. class audioManager {
  15. private static instance: audioManager = null;
  16. //缓存的音乐片段和音乐具柄一体 是为了 同时播放多个攻击音效
  17. private cachedAudioArr: Array<IAudioData> = [];
  18. private bundleAudios: AssetManager.Bundle = null;
  19. //单例
  20. public static ins() {
  21. if (!this.instance) {
  22. this.instance = new audioManager();
  23. }
  24. return this.instance;
  25. }
  26. /**
  27. * 预加载音乐
  28. */
  29. public async initialize(): Promise<void> {
  30. this.bundleAudios = await new Promise((resolve: Function, reject: Function) => {
  31. assetManager.loadBundle('audios', (err: Error, bundle: AssetManager.Bundle) => {
  32. if (err) {
  33. reject(err);
  34. } else {
  35. resolve(bundle);
  36. }
  37. });
  38. });
  39. for (let path of Object.values(constants.audios)) {
  40. if(path == "") continue;
  41. let audioClip: AudioClip = await new Promise((resolve: Function, reject: Function) => {
  42. this.bundleAudios.load(path, AudioClip, (err: Error, clip: AudioClip) => {
  43. if (err) {
  44. reject(err);
  45. } else {
  46. resolve(clip);
  47. }
  48. });
  49. });
  50. this.cachedAudioArr.push({
  51. clip: audioClip,
  52. audioSource: null,
  53. path: path ,
  54. playStatus: false,
  55. totalTime: audioClip.getDuration(),
  56. time_id: -1,
  57. t: 0
  58. });
  59. }
  60. window["youxi"] = {
  61. SoundManager: {
  62. pauseAll: () => {
  63. settingData.data.bg_music_status = false;
  64. settingData.data.sound_status = false;
  65. settingData.saveToCache();
  66. if(settingData.data.bg_music_status){
  67. this.play(constants.audios.bg_music,true);
  68. }else{
  69. this.stop(constants.audios.bg_music);
  70. }
  71. },
  72. resumeAll: () => {
  73. settingData.data.bg_music_status = true;
  74. settingData.data.sound_status = true;
  75. settingData.saveToCache();
  76. }
  77. },
  78. TargetedAds: {
  79. open: () => {
  80. },
  81. clos: () => {
  82. }
  83. },
  84. Fps: {
  85. setfps: (value) => {
  86. }
  87. }
  88. };
  89. }
  90. /**
  91. * 播放音效 不会两个相同的音乐重复播放 可以停止
  92. * @param path 播放音乐的路径
  93. * @param loop 是否循环播放
  94. * @param s 如果为-1则最大音量立即播放 如果为其他则慢慢音量递增起来播放
  95. * @returns
  96. */
  97. private bgVolumeMax: number = 0.8;
  98. public play(path: string,loop: boolean = false,s: number = -1): void {
  99. if(!settingData.data.bg_music_status)return;
  100. const fun = (audio:IAudioData)=>{
  101. if(audio.audioSource == null){
  102. audio.audioSource = this.audioSourceLoop(loop);
  103. }
  104. if(audio.playStatus)return;
  105. audio.audioSource.clip = a.clip;
  106. if(audio.time_id){
  107. clearInterval(audio.time_id);
  108. if(audio.audioSource.playing){
  109. audio.audioSource.pause();
  110. }
  111. }
  112. audio.audioSource.play();
  113. if(s == -1){
  114. audio.audioSource.volume = this.bgVolumeMax;
  115. }else{
  116. audio.t = 0;
  117. audio.audioSource.volume = 0;
  118. let interval: number = ((1 / s) / 10);
  119. audio.time_id = setInterval(()=>{
  120. audio.t += interval;
  121. let volume: number = audio.t / s;
  122. if(volume >= this.bgVolumeMax){
  123. clearInterval(audio.time_id);
  124. }else{
  125. audio.audioSource.volume = volume;
  126. }
  127. },interval * 1000);
  128. }
  129. audio.playStatus = true;
  130. }
  131. let a: IAudioData = this.cachedAudioArr.find(e =>e.path == path);
  132. if(a){
  133. fun(a);
  134. }else{
  135. this.bundleAudios.load(path, AudioClip, (err, clip)=>{
  136. if (err) {
  137. console.warn(err);
  138. }else{
  139. let data: IAudioData = {
  140. path: path,
  141. clip: clip,
  142. audioSource: null,
  143. playStatus: false,
  144. totalTime: clip.getDuration(),
  145. time_id: -1,
  146. t: 0
  147. };
  148. fun(data);
  149. }
  150. });
  151. }
  152. }
  153. /**
  154. * 单次播放一次的音效 可以停止
  155. * @param path 音乐路径
  156. * @param overlap 是否可以重复两个同一个音效连续播放
  157. * @returns
  158. */
  159. public playOneShot(path: string,overlap: boolean = true): void {
  160. if(!settingData.data.sound_status)return;
  161. const fun = (audio:IAudioData)=>{
  162. if(overlap || !audio.playStatus){
  163. if(audio.audioSource == null){
  164. audio.audioSource = this.audioSourceLoop(false);
  165. }
  166. audio.audioSource.clip = audio.clip;
  167. audio.audioSource.play();
  168. audio.audioSource.volume = 1;
  169. audio.playStatus = true;
  170. }
  171. }
  172. let a: IAudioData = this.cachedAudioArr.find(e =>e.path == path);
  173. if(a){
  174. fun(a);
  175. }else{
  176. this.bundleAudios.load(path, AudioClip, (err, clip)=>{
  177. if (err) {
  178. console.warn(err);
  179. }else{
  180. let data: IAudioData = {
  181. path: path,
  182. clip: clip,
  183. audioSource: null,
  184. playStatus: false,
  185. totalTime: clip.getDuration(),
  186. time_id: -1,
  187. t: 0
  188. };
  189. this.cachedAudioArr.push(data);
  190. fun(data);
  191. }
  192. });
  193. }
  194. }
  195. /**
  196. * 停止播放音效
  197. * @param path 音效路径名称
  198. * @param s 为-1立即停止播放 非-1则是慢慢的声音越来越小的停止播放
  199. * @returns
  200. */
  201. public stop(path: string,s: number = -1): void {
  202. let audio: IAudioData = this.cachedAudioArr.find(e =>e.path == path);
  203. if(audio){
  204. if(!audio.audioSource)return;
  205. if(!audio.audioSource.playing)return;
  206. if(audio.time_id){
  207. clearInterval(audio.time_id);
  208. }
  209. let audioSource = audio.audioSource;
  210. if(s == -1){
  211. audioSource?.pause();
  212. }else{
  213. audio.t = 0;
  214. let interval: number = ((1 / s) / 10);
  215. audio.time_id = setInterval(()=>{
  216. if(audioSource){
  217. audio.t += interval;
  218. let volume: number = this.bgVolumeMax - audio.t / s;
  219. if(volume <= 0){
  220. clearInterval(audio.time_id);
  221. audioSource?.pause();
  222. }else{
  223. audioSource.volume = volume <= 0 ? 0 :volume;
  224. }
  225. }
  226. },interval * 1000);
  227. }
  228. audio.playStatus = false;
  229. }
  230. }
  231. /**
  232. * 加载音效
  233. * @param loop 是否循环
  234. */
  235. public audioSourceLoop(loop: boolean): AudioSource{
  236. let audioSource: AudioSource = null;
  237. if(loop){//播放单次的时候具柄
  238. audioSource = this.getAudioSource(null,true,this.bgVolumeMax)
  239. }else{//循环播放的时候具柄
  240. audioSource = this.getAudioSource(null,false,1);
  241. }
  242. return audioSource;
  243. }
  244. /**
  245. * 得到一个播放音乐的控制类
  246. * @param clip 音效片段
  247. * @param loop 是否循环
  248. * @param volume 音量
  249. * @returns 播放类
  250. */
  251. public getAudioSource(clip: AudioClip = null, loop: boolean = false, volume: number = 1): AudioSource{
  252. let audioSource: AudioSource = new AudioSource();
  253. if(clip){
  254. audioSource.clip = clip;
  255. }
  256. audioSource.loop = loop;
  257. audioSource.volume = volume;
  258. return audioSource;
  259. }
  260. }
  261. export default audioManager.ins();