import { _decorator, Component, AudioSource, AudioClip, Node, sys } from 'cc'; const { ccclass, property, menu } = _decorator; @ccclass @menu('Audio/SoundManager') export class SoundManager extends Component { // ==================== 播放器通道 ==================== @property(AudioSource) bgmSource: AudioSource = null!; // 背景音乐 @property(AudioSource) normalSource: AudioSource = null!; // 常规音效(playOneShot) @property(AudioSource) stopableSource: AudioSource = null!; // 可停止音效(如技能)优先使用此音效,多个音效要一起播时使用下面两个备用 @property(AudioSource) stopableSource2: AudioSource = null!; // 可停止音效(如技能) @property(AudioSource) stopableSource3: AudioSource = null!; // 可停止音效(如技能) @property(AudioSource) loopSource: AudioSource = null!; // 循环音效(游戏中) @property(AudioSource) resultLoopSource: AudioSource = null!; // 循环音效(结果界面) // ==================== 音效资源(可选预览) ==================== @property(AudioClip) bgmClip: AudioClip = null!; @property(AudioClip) clickClip: AudioClip = null!; // ==================== 开关状态 ==================== private _musicEnabled: boolean = true; private _soundEnabled: boolean = true; private static readonly KEY_MUSIC = 'SoundManager_Music'; private static readonly KEY_SOUND = 'SoundManager_Sound'; // ==================== 生命周期 ==================== start() { // this.initAudioSources(); this.loadSettings(); this.applyVolume(); // 开关开启时自动播放 BGM // if (this._musicEnabled && this.bgmClip) { // this.playBGM(); // } } private initAudioSources() { // 自动创建缺失的 AudioSource const createSource = (name: string): AudioSource => { const node = new Node(name); node.parent = this.node; const source = node.addComponent(AudioSource); source.playOnAwake = false; return source; }; if (!this.bgmSource) this.bgmSource = createSource('BGM_Source'); if (!this.normalSource) this.normalSource = createSource('Normal_Source'); if (!this.stopableSource) this.stopableSource = createSource('Stopable_Source'); if (!this.loopSource) this.loopSource = createSource('Loop_Source'); } // ==================== 本地设置 ==================== private loadSettings() { const m = sys.localStorage.getItem(SoundManager.KEY_MUSIC); const s = sys.localStorage.getItem(SoundManager.KEY_SOUND); this._musicEnabled = m === null ? true : m === 'true'; this._soundEnabled = s === null ? true : s === 'true'; } private saveSettings() { sys.localStorage.setItem(SoundManager.KEY_MUSIC, this._musicEnabled.toString()); sys.localStorage.setItem(SoundManager.KEY_SOUND, this._soundEnabled.toString()); } private applyVolume() { this.bgmSource.volume = this._musicEnabled ? 1 : 0; // 其他 source 播放时动态控制 } // ==================== 背景音乐 ==================== public playBGM(clip?: AudioClip) { if (!this._musicEnabled) return; const target = clip || this.bgmClip; if (!target) return; this.bgmSource.stop(); this.bgmSource.clip = target; this.bgmSource.loop = true; this.bgmSource.play(); } public stopBGM() { this.bgmSource.stop(); } public pauseBGM() { this.bgmSource.pause(); } public resumeBGM() { if (this._musicEnabled) this.bgmSource.play(); } // ==================== 按钮点击常规音效 ==================== public playClick(clip?: AudioClip) { if (!this._soundEnabled) return; const target = clip || this.clickClip; if (target) { this.normalSource.playOneShot(target, 1.0); } } // ==================== 可停止音效 ==================== public playEffect1(clip?: AudioClip, volume: number = 1) { if (!this._soundEnabled) return; const target = clip; if (!target) return; this.stopableSource.clip = target; this.stopableSource.loop = false; this.stopableSource.volume = volume; this.stopableSource.play(); } public stopEffect1() { this.stopableSource.stop(); } // ==================== 播放可停止音效 ==================== public playEffect2(clip?: AudioClip, volume: number = 1) { if (!this._soundEnabled) return; const target = clip; if (!target) return; this.stopableSource2.clip = target; this.stopableSource2.loop = false; this.stopableSource.volume = volume; this.stopableSource2.play(); } public stopEffect2() { this.stopableSource2.stop(); } // ==================== 播放可停止音效 ==================== public playEffect3(clip?: AudioClip, volume: number = 1) { if (!this._soundEnabled) return; const target = clip; if (!target) return; this.stopableSource3.clip = target; this.stopableSource3.loop = false; this.stopableSource.volume = volume; this.stopableSource3.play(); } public stopEffect3() { this.stopableSource3.stop(); } // ==================== 游戏中循环音效 ==================== public playLoopEffect(clip?: AudioClip, volume?: number) { if (!this._soundEnabled) return; const target = clip; if (!target) return; this.loopSource.clip = target; this.loopSource.loop = true; this.loopSource.volume = volume; this.loopSource.play(); } public stopLoopEffect() { this.loopSource.stop(); } public setLoopEffectVolume(volume?: number) { if (!this._soundEnabled) return; this.loopSource.volume = volume; } // ==================== 结果循环音效 ==================== public playResultLoopEffect(clip?: AudioClip, volume?: number) { if (!this._soundEnabled) return; const target = clip; if (!target) return; this.resultLoopSource.clip = target; this.resultLoopSource.loop = true; this.resultLoopSource.volume = volume; this.resultLoopSource.play(); } public stopResultLoopEffect() { this.resultLoopSource.stop(); } public setResultLoopEffectVolume(volume?: number) { this.resultLoopSource.volume = volume; } // ==================== 全局开关 ==================== public setMusicEnabled(enabled: boolean) { this._musicEnabled = enabled; this.bgmSource.volume = enabled ? 1 : 0; if (!enabled) this.pauseBGM(); else if (this.bgmSource.clip) this.resumeBGM(); this.saveSettings(); } public setSoundEnabled(enabled: boolean) { this._soundEnabled = enabled; if (!enabled) { this.stopEffect1(); this.stopEffect2(); this.stopEffect3(); this.stopLoopEffect(); } this.saveSettings(); } public toggleMusic() { this.setMusicEnabled(!this._musicEnabled); } public toggleSound() { this.setSoundEnabled(!this._soundEnabled); } public get musicEnabled() { return this._musicEnabled; } public get soundEnabled() { return this._soundEnabled; } // ==================== 生命周期结束 ==================== onDestroy() { this.stopBGM(); this.stopEffect1(); this.stopEffect2(); this.stopEffect3(); this.stopLoopEffect(); } }