first commit
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OCES.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// 维护当前激活的游戏状态,在状态变化时全量匹配 Path 表,返回应当播放的 ContainerId。
|
||||
/// </summary>
|
||||
public class MusicStateRouter
|
||||
{
|
||||
// key: StateGroup enum Type,value: 当前激活的 enum 整数值
|
||||
readonly Dictionary<Type, int> m_activeStates = new();
|
||||
|
||||
readonly MusicPathConfig m_musicPaths;
|
||||
readonly AmbiencePathConfig m_ambiencePaths;
|
||||
|
||||
// 上一次匹配到的 PathId,用于 Transition 表的 FromPathId 查询
|
||||
public uint LastMusicPathId { get; private set; }
|
||||
public uint LastAmbiencePathId { get; private set; }
|
||||
|
||||
public MusicStateRouter(MusicPathConfig musicPaths, AmbiencePathConfig ambiencePaths)
|
||||
{
|
||||
this.m_musicPaths = musicPaths;
|
||||
this.m_ambiencePaths = ambiencePaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新某个 StateGroup 的当前值,并重新全量匹配两张 Path 表。
|
||||
/// </summary>
|
||||
public void SetState<TEnum>(TEnum state, out uint musicContainerId, out uint ambienceContainerId)
|
||||
where TEnum : Enum
|
||||
{
|
||||
// Dictionary<Type, int> 天然保证同一 StateGroup 只保留最新值,直接覆盖即可
|
||||
this.m_activeStates[typeof(TEnum)] = Convert.ToInt32(state);
|
||||
|
||||
musicContainerId = MatchBestPath(this.m_musicPaths.MusicPathList(), out uint musicPathId);
|
||||
ambienceContainerId = MatchBestPath(this.m_ambiencePaths.AmbiencePathList(), out uint ambiencePathId);
|
||||
|
||||
LastMusicPathId = musicPathId;
|
||||
LastAmbiencePathId = ambiencePathId;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// 内部匹配逻辑
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 遍历路径列表,找到所有满足当前状态的规则,返回 priority 最小(最高优先级)的那条的 ContainerId。
|
||||
/// </summary>
|
||||
uint MatchBestPath<T>(List<T> paths, out uint matchedPathId) where T : IPathEntry
|
||||
{
|
||||
IPathEntry best = null;
|
||||
foreach (T path in paths)
|
||||
{
|
||||
if (PathMatches(path.Path) && (best == null || path.Priority < best.Priority))
|
||||
best = path;
|
||||
}
|
||||
|
||||
if (best != null)
|
||||
{
|
||||
matchedPathId = best.Id;
|
||||
return best.ContainerId;
|
||||
}
|
||||
|
||||
matchedPathId = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析 path 字符串,判断当前激活状态是否满足该条规则。
|
||||
/// 格式:TypeID1,子状态|TypeID2,子状态……
|
||||
/// 规则中的每一个条件都必须满足(AND 关系)。
|
||||
/// </summary>
|
||||
bool PathMatches(string pathStr)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pathStr))
|
||||
return true; // 空 path 视为无条件匹配(默认兜底规则)
|
||||
|
||||
string[] conditions = pathStr.Split('|');
|
||||
foreach (string condition in conditions)
|
||||
{
|
||||
string[] parts = condition.Split(',');
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
Debug.LogWarning($"[MusicStateRouter] Path格式错误: '{condition}'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!int.TryParse(parts[0].Trim(), out int typeId) ||
|
||||
!int.TryParse(parts[1].Trim(), out int stateValue))
|
||||
{
|
||||
Debug.LogWarning($"[MusicStateRouter] Path解析失败: '{condition}'");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool conditionMet = false;
|
||||
foreach (KeyValuePair<Type, int> kv in this.m_activeStates)
|
||||
{
|
||||
if (StateGroupRegistry.GetTypeId(kv.Key) != typeId || kv.Value != stateValue)
|
||||
continue;
|
||||
conditionMet = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!conditionMet)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// StateGroup 注册表:将 enum Type 映射为策划表中填写的 TypeId 整数
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 程序员在此注册所有 StateGroup enum 与其对应 TypeId 的映射关系。
|
||||
/// 策划在 MusicPath.path 字段中填写的 TypeId 数字必须与此处一致。
|
||||
/// </summary>
|
||||
public static class StateGroupRegistry
|
||||
{
|
||||
static readonly Dictionary<Type, int> s_typeIdMap = new();
|
||||
|
||||
public static void Register<TEnum>(int typeId) where TEnum : Enum
|
||||
{
|
||||
s_typeIdMap[typeof(TEnum)] = typeId;
|
||||
}
|
||||
|
||||
public static int GetTypeId(Type enumType)
|
||||
{
|
||||
if (s_typeIdMap.TryGetValue(enumType, out int id))
|
||||
return id;
|
||||
Debug.LogWarning($"[StateGroupRegistry] 未注册的StateGroup类型: {enumType.Name},请调用StateGroupRegistry.Register<T>()");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// 辅助接口,让泛型方法同时处理 MusicPath 和 AmbiencePath
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
public interface IPathEntry
|
||||
{
|
||||
uint Id { get; }
|
||||
string Path { get; }
|
||||
uint ContainerId { get; }
|
||||
int Priority { get; }
|
||||
}
|
||||
|
||||
public partial class MusicPath : IPathEntry { }
|
||||
public partial class AmbiencePath : IPathEntry { }
|
||||
}
|
||||
Reference in New Issue
Block a user