refactor: 重构 Transition 查询逻辑,移除 PathId 改用 ContainerId 匹配
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OCES.Audio
|
||||
@@ -12,6 +13,7 @@ namespace OCES.Audio
|
||||
readonly AmbienceTransitionConfig m_transitionConfig;
|
||||
readonly MonoBehaviour m_coroutineHost;
|
||||
readonly ChannelFader m_fader;
|
||||
readonly List<AmbienceTransition> m_transitionCandidates = new();
|
||||
|
||||
ContainerPlayHandle m_currentHandle;
|
||||
Coroutine m_transitionCoroutine;
|
||||
@@ -22,7 +24,7 @@ namespace OCES.Audio
|
||||
|
||||
internal AmbienceChannelPlayer(
|
||||
AmbienceTransitionConfig transitionConfig,
|
||||
MusicContainerPlayer player,
|
||||
LongAudioContainerPlayer player,
|
||||
MonoBehaviour coroutineHost)
|
||||
{
|
||||
this.m_transitionConfig = transitionConfig;
|
||||
@@ -34,12 +36,12 @@ namespace OCES.Audio
|
||||
// 公开接口
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
internal void SwitchTo(uint newContainerId, uint fromPathId, uint toPathId)
|
||||
internal void SwitchTo(uint newContainerId)
|
||||
{
|
||||
if (newContainerId == this.m_currentContainerId && this.m_currentHandle != null)
|
||||
return;
|
||||
|
||||
AmbienceTransition transition = ResolveTransition(fromPathId, toPathId);
|
||||
AmbienceTransition transition = ResolveTransition((int)this.m_currentContainerId, (int)newContainerId);
|
||||
|
||||
if (this.m_transitionCoroutine != null)
|
||||
this.m_coroutineHost.StopCoroutine(this.m_transitionCoroutine);
|
||||
@@ -93,26 +95,29 @@ namespace OCES.Audio
|
||||
// 工具
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
AmbienceTransition ResolveTransition(uint fromPathId, uint toPathId)
|
||||
AmbienceTransition ResolveTransition(int sourceContainerId, int destinationContainerId)
|
||||
{
|
||||
// 优先精确匹配
|
||||
uint exactId = fromPathId * 1000 + toPathId;
|
||||
AmbienceTransition exact = this.m_transitionConfig.QueryById(exactId);
|
||||
if (exact != null) return exact;
|
||||
this.m_transitionCandidates.Clear();
|
||||
foreach (AmbienceTransition transition in this.m_transitionConfig.AmbienceTransitionList())
|
||||
{
|
||||
bool sourceMatch = transition.SourceContainerID == sourceContainerId || transition.SourceContainerID < 0;
|
||||
bool destMatch = transition.DestinationContainerID == destinationContainerId || transition.DestinationContainerID < 0;
|
||||
if (sourceMatch && destMatch)
|
||||
{
|
||||
this.m_transitionCandidates.Add(transition);
|
||||
}
|
||||
}
|
||||
|
||||
// From 为任意
|
||||
uint fromWildcard = 999u * 1000 + toPathId;
|
||||
AmbienceTransition fromWild = this.m_transitionConfig.QueryById(fromWildcard);
|
||||
if (fromWild != null) return fromWild;
|
||||
if (this.m_transitionCandidates.Count == 0)
|
||||
return this.m_transitionConfig.QueryById(1);
|
||||
|
||||
// To 为任意
|
||||
uint toWildcard = fromPathId * 1000 + 999u;
|
||||
AmbienceTransition toWild = this.m_transitionConfig.QueryById(toWildcard);
|
||||
if (toWild != null) return toWild;
|
||||
|
||||
// 全通配
|
||||
const uint allWild = 999u * 1000 + 999u;
|
||||
return this.m_transitionConfig.QueryById(allWild);
|
||||
AmbienceTransition best = this.m_transitionCandidates[0];
|
||||
for (int i = 1; i < this.m_transitionCandidates.Count; i++)
|
||||
{
|
||||
if (this.m_transitionCandidates[i].Id > best.Id)
|
||||
best = this.m_transitionCandidates[i];
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace OCES.Audio
|
||||
/// </summary>
|
||||
public class ChannelFader
|
||||
{
|
||||
readonly MusicContainerPlayer m_player;
|
||||
readonly LongAudioContainerPlayer m_player;
|
||||
readonly MonoBehaviour m_coroutineHost;
|
||||
|
||||
internal ContainerPlayHandle CurrentHandle { get; private set; }
|
||||
public uint CurrentContainerId { get; private set; }
|
||||
public float CurrentVolume { get; private set; }
|
||||
|
||||
internal ChannelFader(MusicContainerPlayer player, MonoBehaviour coroutineHost)
|
||||
internal ChannelFader(LongAudioContainerPlayer player, MonoBehaviour coroutineHost)
|
||||
{
|
||||
this.m_player = player;
|
||||
this.m_coroutineHost = coroutineHost;
|
||||
|
||||
+6
-6
@@ -12,7 +12,7 @@ namespace OCES.Audio
|
||||
/// 被 MusicChannelPlayer 和 AmbienceChannelPlayer 共同使用。
|
||||
/// 不持有状态机逻辑,只负责"把这个 Container 播完"。
|
||||
/// </summary>
|
||||
class MusicContainerPlayer
|
||||
class LongAudioContainerPlayer
|
||||
{
|
||||
readonly MusicContainerConfig m_containerConfig;
|
||||
readonly MusicSegmentConfig m_segmentConfig;
|
||||
@@ -33,7 +33,7 @@ namespace OCES.Audio
|
||||
// Random 模式的不放回历史,key = containerId
|
||||
readonly Dictionary<uint, HashSet<uint>> m_randomHistory = new();
|
||||
|
||||
public MusicContainerPlayer(
|
||||
public LongAudioContainerPlayer(
|
||||
MusicContainerConfig containerConfig,
|
||||
MusicSegmentConfig segmentConfig,
|
||||
AudioSourcePool pool,
|
||||
@@ -58,7 +58,7 @@ namespace OCES.Audio
|
||||
MusicContainer container = this.m_containerConfig.QueryById(containerId);
|
||||
if (container == null)
|
||||
{
|
||||
Debug.LogError($"[MusicContainerPlayer] 找不到 ContainerId: {containerId}");
|
||||
Debug.LogError($"[LongAudioContainerPlayer] 找不到 ContainerId: {containerId}");
|
||||
onFinished?.Invoke();
|
||||
return null;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ namespace OCES.Audio
|
||||
Action onFinished)
|
||||
{
|
||||
float effectiveBpm = container.Bpm > 0f ? container.Bpm : inheritedBpm;
|
||||
//Debug.Log($"[MusicContainerPlayer] OnContainerEntered firing, container={container.Id}, subscribers={OnContainerEntered != null}");
|
||||
//Debug.Log($"[LongAudioContainerPlayer] OnContainerEntered firing, container={container.Id}, subscribers={OnContainerEntered != null}");
|
||||
OnContainerEntered?.Invoke(container, effectiveBpm, AudioSettings.dspTime);
|
||||
|
||||
int loopsCompleted = 0;
|
||||
@@ -314,7 +314,7 @@ namespace OCES.Audio
|
||||
MusicSegment segment = this.m_segmentConfig.QueryById(segmentId);
|
||||
if (segment == null)
|
||||
{
|
||||
Debug.LogError($"[MusicContainerPlayer] 找不到 SegmentId: {segmentId}");
|
||||
Debug.LogError($"[LongAudioContainerPlayer] 找不到 SegmentId: {segmentId}");
|
||||
onFinished?.Invoke();
|
||||
return null;
|
||||
}
|
||||
@@ -322,7 +322,7 @@ namespace OCES.Audio
|
||||
AudioClip clip = Resources.Load<AudioClip>($"Audios/{segment.Name}");
|
||||
if (!clip)
|
||||
{
|
||||
Debug.LogError($"[MusicContainerPlayer] 音频文件未找到: {segment.Name}");
|
||||
Debug.LogError($"[LongAudioContainerPlayer] 音频文件未找到: {segment.Name}");
|
||||
onFinished?.Invoke();
|
||||
return null;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OCES.Audio
|
||||
@@ -15,27 +17,21 @@ namespace OCES.Audio
|
||||
readonly MonoBehaviour m_coroutineHost;
|
||||
readonly ChannelFader m_fader;
|
||||
readonly BeatClock m_beatClock;
|
||||
readonly List<MusicTransition> m_transitionCandidates = new();
|
||||
|
||||
Coroutine m_currentFadeInCoroutine;
|
||||
Coroutine m_currentFadeOutCoroutine;
|
||||
|
||||
// 当前正在播放的句柄
|
||||
ContainerPlayHandle m_currentHandle;
|
||||
uint m_currentContainerId;
|
||||
|
||||
|
||||
// 当前播放的 Container(用于读取 bpm/timeSig 做节拍对齐)
|
||||
MusicContainer m_currentContainer;
|
||||
|
||||
// 当前播放开始的时间(用于计算当前播到哪个拍子)
|
||||
double m_playStartTime;
|
||||
|
||||
// 正在进行的 transition 协程(防止重叠)
|
||||
Coroutine m_transitionCoroutine;
|
||||
|
||||
internal MusicChannelPlayer(
|
||||
MusicContainerConfig containerConfig,
|
||||
MusicTransitionConfig transitionConfig,
|
||||
MusicContainerPlayer player,
|
||||
LongAudioContainerPlayer player,
|
||||
MonoBehaviour coroutineHost,
|
||||
Action<uint> onBeat,
|
||||
Action<uint> onBar,
|
||||
@@ -56,14 +52,14 @@ namespace OCES.Audio
|
||||
|
||||
/// <summary>
|
||||
/// 切换到新的 Container。
|
||||
/// fromPathId / toPathId 用于查询 Transition 配置。
|
||||
/// </summary>
|
||||
internal void SwitchTo(uint newContainerId, uint fromPathId, uint toPathId)
|
||||
internal void SwitchTo(uint newContainerId)
|
||||
{
|
||||
if (newContainerId == this.m_currentContainerId && this.m_currentHandle != null)
|
||||
if (newContainerId == this.m_fader.CurrentContainerId && this.m_fader.CurrentHandle != null)
|
||||
return; // 已经在播目标,无需切换
|
||||
|
||||
MusicTransition transition = ResolveTransition(fromPathId, toPathId);
|
||||
|
||||
MusicTransition transition = ResolveTransition((int)this.m_fader.CurrentContainerId, (int)newContainerId);
|
||||
//Debug.Log($"[MusicChannelPlayer] Switch from {this.m_fader.CurrentContainerId} to {newContainerId} with transition {transition.Id}");
|
||||
|
||||
if (this.m_transitionCoroutine != null)
|
||||
this.m_coroutineHost.StopCoroutine(this.m_transitionCoroutine);
|
||||
@@ -181,7 +177,6 @@ namespace OCES.Audio
|
||||
}
|
||||
|
||||
this.m_currentContainer = container;
|
||||
this.m_playStartTime = dspTime;
|
||||
this.m_beatClock.Restart(container, bpm, dspTime);
|
||||
}));
|
||||
yield return this.m_currentFadeInCoroutine;
|
||||
@@ -206,29 +201,31 @@ namespace OCES.Audio
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 查询 Transition 配置,支持精确匹配和 999 通配符。
|
||||
/// ID 规则:FromPathId × 1000 + ToPathId,999 表示任意。
|
||||
/// 查询 Transition 配置,支持精确匹配和 -1 通配符。
|
||||
/// </summary>
|
||||
MusicTransition ResolveTransition(uint fromPathId, uint toPathId)
|
||||
MusicTransition ResolveTransition(int sourceContainerId, int destinationContainerId)
|
||||
{
|
||||
// 优先精确匹配
|
||||
uint exactId = fromPathId * 1000 + toPathId;
|
||||
MusicTransition exact = this.m_transitionConfig.QueryById(exactId);
|
||||
if (exact != null) return exact;
|
||||
this.m_transitionCandidates.Clear();
|
||||
foreach (MusicTransition transition in this.m_transitionConfig.MusicTransitionList())
|
||||
{
|
||||
bool sourceMatch = transition.SourceContainerID == sourceContainerId || transition.SourceContainerID < 0;
|
||||
bool destMatch = transition.DestinationContainerID == destinationContainerId || transition.DestinationContainerID < 0;
|
||||
if (sourceMatch && destMatch)
|
||||
{
|
||||
this.m_transitionCandidates.Add(transition);
|
||||
}
|
||||
}
|
||||
|
||||
// From 为任意
|
||||
uint fromWildcard = 999u * 1000 + toPathId;
|
||||
MusicTransition fromWild = this.m_transitionConfig.QueryById(fromWildcard);
|
||||
if (fromWild != null) return fromWild;
|
||||
if (this.m_transitionCandidates.Count == 0)
|
||||
return this.m_transitionConfig.QueryById(1);
|
||||
|
||||
// To 为任意
|
||||
uint toWildcard = fromPathId * 1000 + 999u;
|
||||
MusicTransition toWild = this.m_transitionConfig.QueryById(toWildcard);
|
||||
if (toWild != null) return toWild;
|
||||
|
||||
// 全通配
|
||||
const uint allWild = 999u * 1000 + 999u;
|
||||
return this.m_transitionConfig.QueryById(allWild);
|
||||
MusicTransition best = this.m_transitionCandidates[0];
|
||||
for (int i = 1; i < this.m_transitionCandidates.Count; i++)
|
||||
{
|
||||
if (this.m_transitionCandidates[i].Id > best.Id)
|
||||
best = this.m_transitionCandidates[i];
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ namespace OCES.Audio
|
||||
internal event Action<uint> OnBar;
|
||||
internal event Action<uint> OnGrid;
|
||||
|
||||
// 记录上一次两个通道各自匹配到的 PathId,用于查 Transition 表
|
||||
uint m_lastMusicPathId;
|
||||
uint m_lastAmbiencePathId;
|
||||
|
||||
|
||||
internal IReadOnlyDictionary<Type, Enum> ActiveStates
|
||||
{
|
||||
@@ -37,12 +35,12 @@ namespace OCES.Audio
|
||||
AudioSourcePool musicPool,
|
||||
AudioSourcePool ambiencePool)
|
||||
{
|
||||
MusicContainerPlayer musicContainerPlayer = new(containers, segments, musicPool, this);
|
||||
MusicContainerPlayer ambientContainerPlayer = new(containers, segments, ambiencePool, this);
|
||||
LongAudioContainerPlayer longAudioContainerPlayer = new(containers, segments, musicPool, this);
|
||||
LongAudioContainerPlayer ambientContainerPlayer = new(containers, segments, ambiencePool, this);
|
||||
|
||||
this.m_stateRouter = new MusicStateRouter(musicPaths, ambiencePaths);
|
||||
this.m_musicChannel = new MusicChannelPlayer(
|
||||
containers, musicTransitions, musicContainerPlayer, this,
|
||||
containers, musicTransitions, longAudioContainerPlayer, this,
|
||||
id => OnBeat?.Invoke(id),
|
||||
id => OnBar?.Invoke(id),
|
||||
id => OnGrid?.Invoke(id));
|
||||
@@ -61,14 +59,8 @@ namespace OCES.Audio
|
||||
out uint musicContainerId,
|
||||
out uint ambienceContainerId);
|
||||
|
||||
uint newMusicPathId = this.m_stateRouter.LastMusicPathId;
|
||||
uint newAmbiencePathId = this.m_stateRouter.LastAmbiencePathId;
|
||||
|
||||
this.m_musicChannel.SwitchTo(musicContainerId, this.m_lastMusicPathId, newMusicPathId);
|
||||
this.m_ambienceChannel.SwitchTo(ambienceContainerId, this.m_lastAmbiencePathId, newAmbiencePathId);
|
||||
|
||||
this.m_lastMusicPathId = newMusicPathId;
|
||||
this.m_lastAmbiencePathId = newAmbiencePathId;
|
||||
this.m_musicChannel.SwitchTo(musicContainerId);
|
||||
this.m_ambienceChannel.SwitchTo(ambienceContainerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user