first commit

This commit is contained in:
2026-03-20 17:55:53 +08:00
commit 41e38311f0
327 changed files with 11557 additions and 0 deletions
@@ -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 Typevalue: 当前激活的 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 { }
}