using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace OCES.Audio { /// /// 与Transition无关的音量/生命周期管理逻辑 /// public class ChannelFader { readonly MusicContainerPlayer m_player; readonly MonoBehaviour m_coroutineHost; public ContainerPlayHandle CurrentHandle { get; private set; } public uint CurrentContainerId { get; private set; } public float CurrentVolume { get; private set; } public ChannelFader(MusicContainerPlayer player, MonoBehaviour coroutineHost) { this.m_player = player; this.m_coroutineHost = coroutineHost; } void StartNew(uint containerId, float startVolume) { CurrentContainerId = containerId; CurrentVolume = startVolume; CurrentHandle = this.m_player.Play(containerId); } public void StopCurrent() { StopHandle(CurrentHandle); CurrentHandle = null; CurrentContainerId = 0; } void StopHandle(ContainerPlayHandle handle) { if (handle == null) return; this.m_player.Stop(handle); } /// /// 淡出分支:fire-and-forget,由调用方 StartCoroutine /// internal IEnumerator FadeOutBranch(ContainerPlayHandle outgoingHandle, float outgoingVolume, ITransitionConfig transition) { if (outgoingHandle == null) yield break; if (transition?.FadeOutOffset > 0f) { Debug.Log($"Waiting for {transition.FadeOutOffset} to fade out."); yield return new WaitForSeconds(transition.FadeOutOffset); } if (transition?.FadeOutTime > 0f ) yield return this.m_coroutineHost.StartCoroutine( FadeOut(outgoingHandle, outgoingVolume, transition.FadeOutTime)); else StopHandle(outgoingHandle); } /// /// 淡入分支:等待 FadeInOffset 后启动新音乐并淡入。 /// 主协程 yield return 此分支,以便 DoTransition 在新音乐就绪后才结束。 /// internal IEnumerator FadeInBranch(uint newContainerId, ITransitionConfig transition, Action onContainerStarted = null) { if (transition?.FadeOutOffset > 0f) { Debug.Log($"Waiting {transition.FadeInOffset} to fade in."); yield return new WaitForSeconds(transition.FadeInOffset); } if (newContainerId == 0) { CurrentHandle = null; CurrentContainerId = 0; yield break; } float startVolume = transition?.FadeOutOffset > 0f ? 0f : 1f; StartNew(newContainerId, startVolume); if (transition?.FadeOutOffset > 0f) { yield return this.m_coroutineHost.StartCoroutine( FadeIn(CurrentHandle, transition.FadeInTime)); } } IEnumerator FadeOut(ContainerPlayHandle handle, float fromVolume, float duration) { Debug.Log($"Fading out in {duration} seconds."); if (handle == null || handle.Cancelled) yield break; float elapsed = 0f; List sources = new(); handle.CollectActiveSources(sources); while (elapsed < duration) { if (handle.Cancelled) break; elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / duration); float vol = Mathf.Lerp(fromVolume, 0f, t); foreach (AudioSource src in sources) if (src) src.volume = vol; yield return null; } StopHandle(handle); } IEnumerator FadeIn(ContainerPlayHandle handle, float duration) { Debug.Log($"Fading In {duration} seconds."); if (handle == null || handle.Cancelled) yield break; float elapsed = 0f; List sources = new(); while (elapsed < duration) { if (handle.Cancelled) yield break; elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / duration); // 每帧重新收集(Blend 模式下新 source 可能中途加入) sources.Clear(); handle.CollectActiveSources(sources); foreach (AudioSource src in sources) if (src) src.volume = Mathf.Lerp(0f, 1f, t); yield return null; } // 确保最终音量准确 sources.Clear(); handle.CollectActiveSources(sources); foreach (AudioSource src in sources) if (src) src.volume = 1f; CurrentVolume = 1f; } } }