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