using System; using System.Collections.Generic; using UnityEngine; namespace OCES.Audio { /// /// 维护当前激活的游戏状态,在状态变化时全量匹配 Path 表,返回应当播放的 ContainerId。 /// public class MusicStateRouter { // key: StateGroup enum Type,value: 当前激活的 enum 整数值 readonly Dictionary 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; } /// /// 更新某个 StateGroup 的当前值,并重新全量匹配两张 Path 表。 /// public void SetState(TEnum state, out uint musicContainerId, out uint ambienceContainerId) where TEnum : Enum { // Dictionary 天然保证同一 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; } // ───────────────────────────────────────────── // 内部匹配逻辑 // ───────────────────────────────────────────── /// /// 遍历路径列表,找到所有满足当前状态的规则,返回 priority 最小(最高优先级)的那条的 ContainerId。 /// uint MatchBestPath(List 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; } /// /// 解析 path 字符串,判断当前激活状态是否满足该条规则。 /// 格式:TypeID1,子状态|TypeID2,子状态…… /// 规则中的每一个条件都必须满足(AND 关系)。 /// 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 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 整数 // ───────────────────────────────────────────── /// /// 程序员在此注册所有 StateGroup enum 与其对应 TypeId 的映射关系。 /// 策划在 MusicPath.path 字段中填写的 TypeId 数字必须与此处一致。 /// public static class StateGroupRegistry { static readonly Dictionary s_typeIdMap = new(); public static void Register(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()"); 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 { } }