From 506c26f94586fa6f2d6189d623ecdf3e797bd003 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:49:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(audio):=20=E5=AE=9E=E7=8E=B0=E9=9F=B3?= =?UTF-8?q?=E9=A2=91=E7=AE=A1=E7=90=86=E7=B3=BB=E7=BB=9F=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加音频管理器抽象基类和接口定义 - 支持背景音乐、音效和3D音效播放 - 实现音量控制和音频淡入淡出效果 - 提供音频播放器池化管理机制 - 支持通过资源ID或路径播放音频 - 实现主音量、音乐音量和音效音量独立控制 - 添加音频播放状态检测功能 - 支持低通滤波器和混响效果设置 - 实现系统资源自动清理机制 --- .../system/AbstractAudioManagerSystem.cs | 447 ++++++++++++++++++ .../system/IAudioManagerSystem.cs | 101 ++++ 2 files changed, 548 insertions(+) create mode 100644 GFramework.Core.Godot/system/AbstractAudioManagerSystem.cs create mode 100644 GFramework.Core.Godot/system/IAudioManagerSystem.cs diff --git a/GFramework.Core.Godot/system/AbstractAudioManagerSystem.cs b/GFramework.Core.Godot/system/AbstractAudioManagerSystem.cs new file mode 100644 index 0000000..87181fc --- /dev/null +++ b/GFramework.Core.Godot/system/AbstractAudioManagerSystem.cs @@ -0,0 +1,447 @@ +using GFramework.Core.extensions; +using GFramework.Core.system; +using Godot; + +namespace GFramework.Core.Godot.system; + +/// +/// 音频管理器抽象基类,提供音频播放的基础实现 +/// +public abstract class AbstractAudioManagerSystem : AbstractSystem, IAudioManagerSystem +{ + /// + /// 音频资源加载系统依赖 + /// + protected IResourceLoadSystem? ResourceLoadSystem; + + /// + /// 资源目录系统依赖 + /// + protected IAssetCatalogSystem? AssetCatalogSystem; + + /// + /// 背景音乐播放器 + /// + protected AudioStreamPlayer? MusicPlayer; + + /// + /// 音效播放器列表 + /// + protected readonly List SoundPlayers = []; + + /// + /// 可用音效播放器队列 + /// + protected readonly Queue AvailableSoundPlayers = new(); + + /// + /// 3D音效播放器列表 + /// + protected readonly List Sound3DPlayers = []; + + /// + /// 可用3D音效播放器队列 + /// + protected readonly Queue AvailableSound3DPlayers = new(); + + /// + /// 资源工厂系统依赖 + /// + protected IResourceFactorySystem? ResourceFactorySystem; + + /// + /// 背景音乐音量 + /// + protected float MusicVolume = 1.0f; + + /// + /// 音效音量 + /// + protected float SoundVolume = 1.0f; + + /// + /// 主音量 + /// + protected float MasterVolume = 1.0f; + + /// + /// 特效音量 + /// + protected float SfxVolume = 1.0f; + + /// + /// 语音音量 + /// + protected float VoiceVolume = 1.0f; + + /// + /// 环境音量 + /// + protected float AmbientVolume = 1.0f; + + /// + /// 音乐淡入淡出动画 + /// + protected Tween? MusicFadeTween; + + /// + /// 最大同时播放的音效数量 + /// + protected const int MaxSoundPlayers = 10; + + /// + /// 最大同时播放的3D音效数量 + /// + protected const int MaxSound3DPlayers = 5; + + /// + /// 所有者节点的抽象属性 + /// + protected abstract Node Owner { get; } + + /// + /// 系统初始化方法 + /// + protected override void OnInit() + { + // 获取依赖的系统 + ResourceLoadSystem = this.GetSystem(); + AssetCatalogSystem = this.GetSystem(); + ResourceFactorySystem = this.GetSystem(); + + // 初始化背景音乐播放器 + MusicPlayer = new AudioStreamPlayer(); + Owner.AddChild(MusicPlayer); + + // 预创建音效播放器池 + for (var i = 0; i < MaxSoundPlayers; i++) + { + var soundPlayer = new AudioStreamPlayer(); + Owner.AddChild(soundPlayer); + soundPlayer.Finished += () => OnSoundFinished(soundPlayer); + SoundPlayers.Add(soundPlayer); + AvailableSoundPlayers.Enqueue(soundPlayer); + } + + // 预创建3D音效播放器池 + for (var i = 0; i < MaxSound3DPlayers; i++) + { + var sound3DPlayer = new AudioStreamPlayer3D(); + Owner.AddChild(sound3DPlayer); + sound3DPlayer.Finished += () => OnSound3DFinished(sound3DPlayer); + Sound3DPlayers.Add(sound3DPlayer); + AvailableSound3DPlayers.Enqueue(sound3DPlayer); + } + } + + /// + /// 当音效播放完成时的回调 + /// + /// 完成播放的音频播放器 + private void OnSoundFinished(AudioStreamPlayer player) + { + // 将播放器放回可用队列 + AvailableSoundPlayers.Enqueue(player); + } + + /// + /// 当3D音效播放完成时的回调 + /// + /// 完成播放的3D音频播放器 + private void OnSound3DFinished(AudioStreamPlayer3D player) + { + // 将播放器放回可用队列 + AvailableSound3DPlayers.Enqueue(player); + } + + /// + /// 播放背景音乐 + /// + /// 音频文件路径 + /// 音量大小,范围0-1 + /// 是否循环播放 + public virtual void PlayMusic(string audioPath, float volume = 1.0f, bool loop = true) + { + var audioStream = ResourceLoadSystem?.LoadResource(audioPath); + if (audioStream == null || MusicPlayer == null) return; + + // 停止当前正在进行的淡入淡出效果 + MusicFadeTween?.Kill(); + + MusicPlayer.Stream = audioStream; + MusicPlayer.VolumeDb = LinearToDb(volume * MusicVolume * MasterVolume); + MusicPlayer.Play(); + } + + /// + /// 通过资源ID播放背景音乐 + /// + /// 音乐资源ID + /// 音量大小,范围0-1 + /// 是否循环播放 + public virtual void PlayMusic(AssetCatalog.ResourceId musicId, float volume = 1.0f, bool loop = true) + { + PlayMusic(musicId.Path, volume, loop); + } + + /// + /// 播放音效 + /// + /// 音频文件路径 + /// 音量大小,范围0-1 + /// 音调调整 + public virtual void PlaySound(string audioPath, float volume = 1.0f, float pitch = 1.0f) + { + if (AvailableSoundPlayers.Count == 0) return; + + var audioStream = ResourceLoadSystem?.LoadResource(audioPath); + if (audioStream == null) return; + + var player = AvailableSoundPlayers.Dequeue(); + player.Stream = audioStream; + player.VolumeDb = LinearToDb(volume * SoundVolume * MasterVolume); + player.Play(); + } + + /// + /// 通过资源ID播放音效 + /// + /// 音效资源ID + /// 音量大小,范围0-1 + /// 音调调整 + public virtual void PlaySound(AssetCatalog.ResourceId soundId, float volume = 1.0f, float pitch = 1.0f) + { + PlaySound(soundId.Path, volume, pitch); + } + + /// + /// 播放3D音效 + /// + /// 音频文件路径 + /// 3D空间中的位置 + /// 音量大小,范围0-1 + public virtual void PlaySound3D(string audioPath, Vector3 position, float volume = 1.0f) + { + if (AvailableSound3DPlayers.Count == 0) return; + + var audioStream = ResourceLoadSystem?.LoadResource(audioPath); + if (audioStream == null) return; + + var player = AvailableSound3DPlayers.Dequeue(); + player.Stream = audioStream; + player.VolumeDb = LinearToDb(volume * SoundVolume * MasterVolume); + player.Position = position; + player.Play(); + } + + /// + /// 停止背景音乐 + /// + public virtual void StopMusic() + { + MusicFadeTween?.Kill(); + MusicPlayer?.Stop(); + } + + /// + /// 暂停背景音乐 + /// + public virtual void PauseMusic() + { + MusicFadeTween?.Kill(); + // todo 需要记录音乐播放位置,以便恢复播放时从正确位置开始 + } + + /// + /// 恢复背景音乐播放 + /// + public virtual void ResumeMusic() + { + MusicPlayer?.Play(); + } + + /// + /// 设置背景音乐音量 + /// + /// 音量大小,范围0-1 + public virtual void SetMusicVolume(float volume) + { + MusicVolume = volume; + if (MusicPlayer != null) + { + MusicPlayer.VolumeDb = LinearToDb(MusicVolume * MasterVolume); + } + } + + /// + /// 设置音效音量 + /// + /// 音量大小,范围0-1 + public virtual void SetSoundVolume(float volume) + { + SoundVolume = volume; + } + + /// + /// 设置主音量 + /// + /// 音量大小,范围0-1 + public virtual void SetMasterVolume(float volume) + { + MasterVolume = volume; + + // 更新音乐音量 + if (MusicPlayer != null) + { + MusicPlayer.VolumeDb = LinearToDb(MusicVolume * MasterVolume); + } + } + + /// + /// 设置SFX音量 + /// + /// 音量大小,范围0-1 + public virtual void SetSfxVolume(float volume) + { + SfxVolume = volume; + } + + /// + /// 设置语音音量 + /// + /// 音量大小,范围0-1 + public virtual void SetVoiceVolume(float volume) + { + VoiceVolume = volume; + } + + /// + /// 设置环境音量 + /// + /// 音量大小,范围0-1 + public virtual void SetAmbientVolume(float volume) + { + AmbientVolume = volume; + } + + /// + /// 检查背景音乐是否正在播放 + /// + /// 正在播放返回true,否则返回false + public virtual bool IsMusicPlaying() + { + return MusicPlayer?.Playing ?? false; + } + + /// + /// 淡入背景音乐 + /// + /// 音频文件路径 + /// 淡入持续时间(秒) + /// 目标音量 + public virtual void FadeInMusic(string audioPath, float duration, float volume = 1.0f) + { + var audioStream = ResourceLoadSystem?.LoadResource(audioPath); + if (audioStream == null || MusicPlayer == null) return; + + // 停止当前正在进行的淡入淡出效果 + MusicFadeTween?.Kill(); + + MusicPlayer.Stream = audioStream; + MusicPlayer.VolumeDb = LinearToDb(0.0f); // 初始音量为0 + MusicPlayer.Play(); + + // 创建淡入动画 + MusicFadeTween = Owner.CreateTween(); + MusicFadeTween.TweenProperty(MusicPlayer, "volume_db", LinearToDb(volume * MusicVolume * MasterVolume), + duration); + } + + /// + /// 淡出背景音乐 + /// + /// 淡出持续时间(秒) + public virtual void FadeOutMusic(float duration) + { + if (MusicPlayer == null) return; + + // 停止当前正在进行的淡入淡出效果 + MusicFadeTween?.Kill(); + + // 创建淡出动画 + MusicFadeTween = Owner.CreateTween(); + MusicFadeTween.TweenProperty(MusicPlayer, "volume_db", LinearToDb(0.0f), duration); + MusicFadeTween.TweenCallback(Callable.From(() => MusicPlayer.Stop())); + } + + /// + /// 设置低通滤波器强度 + /// + /// 滤波器强度,范围0-1 + public virtual void SetLowPassFilter(float amount) + { + // TODO: 实现低通滤波器效果 + // 可以通过AudioEffectLowPassFilter实现 + } + + /// + /// 设置音频混响效果 + /// + /// 房间大小 + /// 阻尼 + /// 湿声级别 + public virtual void SetReverb(float roomSize, float damping, float wetLevel) + { + // TODO: 实现音频混响效果 + // 可以通过AudioEffectReverb实现 + } + + /// + /// 将线性音量值转换为分贝值 + /// + /// 线性音量值(0-1) + /// 分贝值 + protected static float LinearToDb(float linear) + { + return linear > 0 ? 20 * Mathf.Log(linear) : -100; + } + + /// + /// 将分贝值转换为线性音量值 + /// + /// 分贝值 + /// 线性音量值(0-1) + protected static float DbToLinear(float db) + { + return db > -100 ? Mathf.Exp(db / 20) : 0; + } + + /// + /// 系统销毁时清理资源 + /// + protected void OnDestroy() + { + // 停止并清理淡入淡出动画 + MusicFadeTween?.Kill(); + + // 清理音乐播放器 + MusicPlayer?.QueueFree(); + + // 清理音效播放器池 + foreach (var player in SoundPlayers) + { + player.QueueFree(); + } + + // 清理3D音效播放器池 + foreach (var player in Sound3DPlayers) + { + player.QueueFree(); + } + + SoundPlayers.Clear(); + AvailableSoundPlayers.Clear(); + Sound3DPlayers.Clear(); + AvailableSound3DPlayers.Clear(); + } +} \ No newline at end of file diff --git a/GFramework.Core.Godot/system/IAudioManagerSystem.cs b/GFramework.Core.Godot/system/IAudioManagerSystem.cs new file mode 100644 index 0000000..47cf1f1 --- /dev/null +++ b/GFramework.Core.Godot/system/IAudioManagerSystem.cs @@ -0,0 +1,101 @@ +using GFramework.Core.system; +using Godot; + +namespace GFramework.Core.Godot.system; + +/// +/// 音频管理器系统接口,用于统一管理背景音乐和音效的播放 +/// +public interface IAudioManagerSystem : ISystem +{ + /// + /// 播放背景音乐 + /// + /// 音频文件路径 + /// 音量大小,范围0-1 + /// 是否循环播放 + void PlayMusic(string audioPath, float volume = 1.0f, bool loop = true); + + /// + /// 播放音效 + /// + /// 音频文件路径 + /// 音量大小,范围0-1 + /// 音调调整 + void PlaySound(string audioPath, float volume = 1.0f, float pitch = 1.0f); + + /// + /// 停止背景音乐 + /// + void StopMusic(); + + /// + /// 暂停背景音乐 + /// + void PauseMusic(); + + /// + /// 恢复背景音乐播放 + /// + void ResumeMusic(); + + /// + /// 设置背景音乐音量 + /// + /// 音量大小,范围0-1 + void SetMusicVolume(float volume); + + /// + /// 设置音效音量 + /// + /// 音量大小,范围0-1 + void SetSoundVolume(float volume); + + /// + /// 设置主音量 + /// + /// 音量大小,范围0-1 + void SetMasterVolume(float volume); + + /// + /// 检查背景音乐是否正在播放 + /// + /// 正在播放返回true,否则返回false + bool IsMusicPlaying(); + + /// + /// 淡入背景音乐 + /// + /// 音频文件路径 + /// 淡入持续时间(秒) + /// 目标音量 + void FadeInMusic(string audioPath, float duration, float volume = 1.0f); + + /// + /// 淡出背景音乐 + /// + /// 淡出持续时间(秒) + void FadeOutMusic(float duration); + + /// + /// 播放3D音效 + /// + /// 音频文件路径 + /// 3D空间中的位置 + /// 音量大小,范围0-1 + void PlaySound3D(string audioPath, Vector3 position, float volume = 1.0f); + + /// + /// 设置低通滤波器强度 + /// + /// 滤波器强度,范围0-1 + void SetLowPassFilter(float amount); + + /// + /// 设置音频混响效果 + /// + /// 房间大小 + /// 阻尼 + /// 湿声级别 + void SetReverb(float roomSize, float damping, float wetLevel); +} \ No newline at end of file