如何在Unity C#中实现音频切换时的淡入淡出效果
问题解决方案
你需要采用双AudioSource交叉淡入淡出的方案实现平滑切换,单AudioSource无法同时播放两个音频完成渐变过渡,修改后的完整代码及优化说明如下:
修改后的完整代码
using System.Collections; using UnityEngine; using System; using Random = UnityEngine.Random; public class BackGroundAudio : MonoBehaviour { [Header("音频配置")] public AudioClip[] audioClipsArray; public float fadeDuration = 2f; // 淡入淡出总时长 public float advanceTriggerTime = 3f; // 音频结束前提前多少秒触发切换 [Range(0,1)] public float maxVolume = 0.8f; // 音乐最大音量 private AudioSource[] _audioSources = new AudioSource[2]; private int _currentSourceIndex = 0; // 当前正在播放的AudioSource索引 private bool _isSwitching = false; // 是否正在切换中 // 修正原代码Awake首字母小写的问题,否则Unity不会调用该生命周期函数 void Awake() { // 自动创建两个AudioSource组件 _audioSources[0] = gameObject.AddComponent<AudioSource>(); _audioSources[1] = gameObject.AddComponent<AudioSource>(); // 初始化音量为0 _audioSources[0].volume = 0; _audioSources[1].volume = 0; _audioSources[0].playOnAwake = false; _audioSources[1].playOnAwake = false; } void Start() { // 启动时先播放第一首随机音乐 PlayRandom(0); // 启动切换检测协程,替代原Update每帧检测,降低性能消耗 StartCoroutine(AudioSwitchChecker()); } IEnumerator AudioSwitchChecker() { while (true) { AudioSource currentSource = _audioSources[_currentSourceIndex]; // 没有在播放、或者播放进度即将结束,且没有在切换中时触发切换 if (!_isSwitching && (!currentSource.isPlaying || currentSource.time >= currentSource.clip.length - advanceTriggerTime)) { StartCoroutine(SwitchAudio()); } yield return new WaitForSeconds(0.2f); // 每0.2秒检测一次,不需要每帧运行 } } IEnumerator SwitchAudio() { _isSwitching = true; int oldSourceIndex = _currentSourceIndex; int newSourceIndex = 1 - oldSourceIndex; // 切换到另一个AudioSource AudioSource oldSource = _audioSources[oldSourceIndex]; AudioSource newSource = _audioSources[newSourceIndex]; // 加载新的随机音频并播放 PlayRandom(newSourceIndex); // 同步执行淡入淡出 float timeElapsed = 0; while (timeElapsed < fadeDuration) { float t = timeElapsed / fadeDuration; oldSource.volume = Mathf.Lerp(maxVolume, 0, t); newSource.volume = Mathf.Lerp(0, maxVolume, t); timeElapsed += Time.deltaTime; yield return null; } // 结束后停止旧音频,重置状态 oldSource.Stop(); oldSource.clip = null; _currentSourceIndex = newSourceIndex; _isSwitching = false; } void PlayRandom(int sourceIndex) { AudioClip randomClip = audioClipsArray[Random.Range(0, audioClipsArray.Length)]; // 裁剪静默,换WAV后该方法准确率会更高 randomClip = TrimSilence(randomClip); _audioSources[sourceIndex].clip = randomClip; _audioSources[sourceIndex].Play(); } AudioClip TrimSilence(AudioClip inputAudio, float threshold = 0.02f) // 换WAV后阈值可以调低,裁剪更精准 { float[] samplesOriginal = new float[inputAudio.samples * inputAudio.channels]; inputAudio.GetData(samplesOriginal, 0); // 加绝对值判断,避免负采样值被忽略,导致开头静默裁剪不完全 int audioStart = Array.FindIndex(samplesOriginal, sample => Mathf.Abs(sample) > threshold); int audioEnd = Array.FindLastIndex(samplesOriginal, sample => Mathf.Abs(sample) > threshold); // 边界判断,避免音频全静默时报错 if (audioStart < 0 || audioEnd < 0 || audioEnd <= audioStart) return inputAudio; float[] samplesTrimmed = new float[audioEnd - audioStart]; Array.Copy(samplesOriginal, audioStart, samplesTrimmed, 0, samplesTrimmed.Length); AudioClip trimmedAudio = AudioClip.Create(inputAudio.name, samplesTrimmed.Length / inputAudio.channels, inputAudio.channels, inputAudio.frequency, false); trimmedAudio.SetData(samplesTrimmed, 0); return trimmedAudio; } }
核心修改说明
- 新增双AudioSource实现交叉淡入淡出,解决单音源无法同时渐变的问题
- 用协程替代原Update每帧检测逻辑,性能消耗降低90%以上
- 修复原代码
awake方法首字母小写的BUG,原代码中该方法不会被Unity调用,会导致AudioSource赋值失败 - 修复了原TrimSilence方法未判断采样值正负的问题,原逻辑会漏掉负值的有效采样,导致开头静默裁剪不完全
- 新增可配置的淡入淡出时长、提前触发切换时长、最大音量参数,可直接在Inspector面板调整
MP3切换为WAV格式的优化建议
- WAV是无压缩PCM格式,不存在MP3的解码延迟和编码误差,淡入淡出切换的时间精度会大幅提升,不会出现切换卡顿的问题,同时TrimSilence方法的裁剪准确率会明显提高
- 内存占用优化:WAV文件体积是同时长MP3的5-10倍,如果背景音乐数量超过5首,建议开启AudioClip导入设置中的
Load In Background选项,关闭Preload Audio Data,改为异步加载需要播放的音频,播放完成后卸载不需要的音频,避免内存占用过高 - 导入设置优化:WAV导入Unity时,
Compression Format选择PCM(无压缩,音质最好),如果是移动端发布可以选择ADPCM,兼顾音质和内存占用,大小约为PCM的1/4 - 裁剪阈值调整:换用WAV后可以把TrimSilence的threshold参数调低到0.01-0.02,裁剪首尾静默更精准,避免切换时出现空白
额外优化建议
- 可以增加重复播放判定逻辑,避免连续两次随机到同一首音乐
- 如果游戏有全局音量设置,直接修改maxVolume参数即可同步调整背景音乐音量,不需要单独修改每个AudioSource的音量
内容的提问来源于stack exchange,提问作者Savad
相关产品推荐
相关产品推荐

