| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- import { AudioClip, AudioSource, director, Node } from 'cc';
- import { LOCAL_MUSIC_SWITCH, LOCAL_SOUND_SWITCH } from '@scripts/constants/Localkey';
- import { BUNDLE_ID } from '@scripts/define/ResDefine';
- import { localStorageGet, localStorageSet } from '@scripts/utils/Helper';
- import { ResourceManager } from '../resource/ResourceManager';
- const DEFAULT_SWITCH_VALUE = '1';
- export interface SoundConfig {
- path: string;
- bundle: string;
- }
- /** 新音频资源在此登记,播放时传入对应 key 即可。 */
- export const SOUND_CONFIGS: Record<string, SoundConfig> = {
- bgm: { path: 'sounds/bgm', bundle: BUNDLE_ID.Lobby },
- button: { path: 'sounds/button', bundle: BUNDLE_ID.Lobby },
- };
- /** 管理跨场景的背景音乐、音效及其本地开关。 */
- export class SoundManager {
- private readonly musicSource: AudioSource;
- private readonly soundSource: AudioSource;
- private readonly clips = new Map<string, AudioClip>();
- private _musicEnabled: boolean;
- private _soundEnabled: boolean;
- constructor(private readonly resources: ResourceManager) {
- this._musicEnabled = this.readSwitch(LOCAL_MUSIC_SWITCH);
- this._soundEnabled = this.readSwitch(LOCAL_SOUND_SWITCH);
- const root = new Node('SoundManager');
- const musicNode = new Node('Music');
- const soundNode = new Node('Sound');
- root.addChild(musicNode);
- root.addChild(soundNode);
- this.musicSource = musicNode.addComponent(AudioSource);
- this.musicSource.playOnAwake = false;
- this.soundSource = soundNode.addComponent(AudioSource);
- this.soundSource.playOnAwake = false;
- director.addPersistRootNode(root);
- }
- get musicEnabled(): boolean {
- return this._musicEnabled;
- }
- set musicEnabled(enabled: boolean) {
- this.setMusicEnabled(enabled);
- }
- get soundEnabled(): boolean {
- return this._soundEnabled;
- }
- set soundEnabled(enabled: boolean) {
- this.setSoundEnabled(enabled);
- }
- /**
- * 预加载指定音频;不传 key 时预加载全部已配置音频。
- */
- async preload(keys: string | readonly string[] = Object.keys(SOUND_CONFIGS)): Promise<void> {
- const soundKeys = typeof keys === 'string' ? [keys] : keys;
- await Promise.all(soundKeys.map((key) => this.loadClip(key)));
- }
- /** 播放背景音乐。关闭音乐时会记住当前曲目,重新开启后自动继续播放。 */
- async playMusic(key: string, loop = true, volume = 1): Promise<void> {
- const clip = await this.loadClip(key);
- if (!clip) return;
- const shouldPlay = this.musicSource.clip !== clip || !this.musicSource.playing;
- this.musicSource.clip = clip;
- this.musicSource.loop = loop;
- this.musicSource.volume = this.normalizeVolume(volume);
- if (this._musicEnabled && shouldPlay) {
- this.musicSource.play();
- }
- }
- /** 播放一次音效;允许同一音效重叠播放。 */
- async playSound(key: string, volume = 1): Promise<void> {
- if (!this._soundEnabled) return;
- const clip = await this.loadClip(key);
- if (!clip || !this._soundEnabled) return;
- this.soundSource.playOneShot(clip, this.normalizeVolume(volume));
- }
- setMusicEnabled(enabled: boolean): void {
- this._musicEnabled = Boolean(enabled);
- localStorageSet(LOCAL_MUSIC_SWITCH, this._musicEnabled ? '1' : '0');
- if (this._musicEnabled) {
- if (this.musicSource.clip) this.musicSource.play();
- } else {
- this.musicSource.pause();
- }
- }
- setSoundEnabled(enabled: boolean): void {
- this._soundEnabled = Boolean(enabled);
- localStorageSet(LOCAL_SOUND_SWITCH, this._soundEnabled ? '1' : '0');
- }
- private readSwitch(key: string): boolean {
- const value = String(localStorageGet(key, DEFAULT_SWITCH_VALUE)).toLowerCase();
- return value !== '0' && value !== 'false';
- }
- private async loadClip(key: string): Promise<AudioClip | null> {
- const cachedClip = this.clips.get(key);
- if (cachedClip?.isValid) return cachedClip;
- const config = SOUND_CONFIGS[key];
- if (!config) {
- console.warn(`[SoundManager] Unknown sound key: ${key}`);
- return null;
- }
- const clip = await this.resources.load(config.bundle, config.path, AudioClip);
- if (clip) this.clips.set(key, clip);
- return clip;
- }
- private normalizeVolume(volume: number): number {
- if (!Number.isFinite(volume)) return 1;
- return Math.min(1, Math.max(0, volume));
- }
- playBgm() {
- this.playMusic('bgm');
- }
- playBtnClick() {
- this.playSound("button")
- }
- }
|