feat: Add switch container functionality
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OCES.Audio
|
||||
{
|
||||
public partial class AudioObjectConfig
|
||||
{
|
||||
Dictionary<uint, Dictionary<int, uint>> m_switchMapping;
|
||||
|
||||
internal void PreParseSwitchMappings()
|
||||
{
|
||||
this.m_switchMapping = new Dictionary<uint, Dictionary<int, uint>>();
|
||||
foreach (AudioObject audioObject in AudioObjectList())
|
||||
{
|
||||
if (audioObject.ContainerType != ContainerType.Switch) continue;
|
||||
this.m_switchMapping[audioObject.Id] = ParseSwitchMapping(audioObject);
|
||||
}
|
||||
}
|
||||
|
||||
public AudioObject GetMappingResult(uint switchContainerId, Enum enumState)
|
||||
{
|
||||
if (!this.m_switchMapping.TryGetValue(switchContainerId, out Dictionary<int, uint> switchMapping))
|
||||
return null;
|
||||
|
||||
return switchMapping.TryGetValue(enumState.GetHashCode(), out uint audioObjectId) ? QueryById(audioObjectId) : null;
|
||||
|
||||
}
|
||||
|
||||
Dictionary<int, uint> ParseSwitchMapping(AudioObject switchContainer)
|
||||
{
|
||||
Dictionary<int, uint> switchMapping = new();
|
||||
foreach (string name in switchContainer.Name)
|
||||
{
|
||||
string[] parts = name.Split(',');
|
||||
if(parts.Length != 2)
|
||||
{
|
||||
Debug.LogWarning($"[AudioSystem] 无法解析 Switch Container {switchContainer.Id} 的映射关系!请检查表");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!int.TryParse(parts[0].Trim(), out int stateValue))
|
||||
{
|
||||
Debug.LogWarning($"[AudioSystem] 无法解析 映射关系!请查表");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!uint.TryParse(parts[1].Trim(), out uint childId))
|
||||
{
|
||||
Debug.LogWarning("");
|
||||
continue;
|
||||
}
|
||||
|
||||
switchMapping.Add(stateValue, childId);
|
||||
}
|
||||
return switchMapping;
|
||||
}
|
||||
|
||||
internal AudioObject GetDefaultSwitchOrFallback(AudioObject switchContainer)
|
||||
{
|
||||
if (switchContainer.DefaultSwitchId == 0)
|
||||
return null;
|
||||
AudioObject defaultChildAudioObject = QueryById(switchContainer.DefaultSwitchId);
|
||||
if (defaultChildAudioObject != null)
|
||||
return defaultChildAudioObject;
|
||||
|
||||
Debug.LogWarning($"[AudioSystem] DefaultSwitch AudioObject {switchContainer.DefaultSwitchId} 不存在。");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68c35969f92d4bb0ba3239d852115c53
|
||||
timeCreated: 1776244015
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
@@ -9,7 +10,9 @@ namespace OCES.Audio
|
||||
public class AudioSystem : MonoBehaviour
|
||||
{
|
||||
public static AudioSystem Instance { get; private set; }
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
public IReadOnlyDictionary<Type, Enum> ActiveStates { get; private set; }
|
||||
|
||||
const string k_audioConfigPath = "AudioData";
|
||||
const string k_audioResourcePath = "Audios";
|
||||
|
||||
@@ -20,7 +23,7 @@ namespace OCES.Audio
|
||||
AudioGroupConfig m_groups;
|
||||
AudioMixer m_mixer;
|
||||
Tween m_lowpassTween;
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// 公开接口
|
||||
// ─────────────────────────────────────────────
|
||||
@@ -36,6 +39,16 @@ namespace OCES.Audio
|
||||
|
||||
public void Play(AudioObject audioObject, Action onPlay = null)
|
||||
{
|
||||
if (audioObject.ContainerType == ContainerType.Switch)
|
||||
{
|
||||
audioObject = ResolveSwitchContainer(audioObject);
|
||||
if (audioObject == null)
|
||||
{
|
||||
Debug.Log("[AudioSystem] 无法解析Switch Container,检查配置表!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.m_sfxSystem.TryPlay(audioObject, onPlay);
|
||||
}
|
||||
|
||||
@@ -43,8 +56,7 @@ namespace OCES.Audio
|
||||
{
|
||||
Play((uint)audioId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Obsolete("Use Play(uint) instead")]
|
||||
public void Play(string audioName)
|
||||
{
|
||||
@@ -108,6 +120,7 @@ namespace OCES.Audio
|
||||
public void SetState<TEnum>(TEnum state) where TEnum : Enum
|
||||
{
|
||||
this.m_musicSystem.OnStateChanged(state);
|
||||
ActiveStates = this.m_musicSystem.ActiveStates;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
@@ -133,6 +146,7 @@ namespace OCES.Audio
|
||||
AudioSourcePool sfxPool = new(sfxPoolRoot.transform); // 不传 mixer group,让 SfxSystem 自己设置
|
||||
this.m_sfxSystem = gameObject.AddComponent<SfxSystem>();
|
||||
this.m_audioObjects = AudioConfigLoader.Load<AudioObjectConfig>($"{k_audioConfigPath}/AudioObject");
|
||||
this.m_audioObjects.PreParseSwitchMappings();
|
||||
this.m_groups = AudioConfigLoader.Load<AudioGroupConfig>($"{k_audioConfigPath}/AudioGroup");
|
||||
this.m_sfxSystem.Initialize(this.m_groups, this.m_mixer, sfxPool); // 传入 pool
|
||||
|
||||
@@ -172,10 +186,38 @@ namespace OCES.Audio
|
||||
// ── 注册 StateGroup ──
|
||||
EnumIds.RegisterAllGameState();
|
||||
|
||||
ActiveStates = new Dictionary<Type, Enum>();
|
||||
|
||||
// ── 启动默认音乐与环境音 ──
|
||||
// 触发一次初始状态,让音乐系统从默认状态开始匹配
|
||||
//SetState(GameState.Home);
|
||||
}
|
||||
|
||||
AudioObject ResolveSwitchContainer(AudioObject switchContainer)
|
||||
{
|
||||
// 遍历 ActiveStates 找到 TypeId 匹配的枚举类型
|
||||
Enum currentStateValue = null;
|
||||
bool foundGroup = false;
|
||||
foreach (KeyValuePair<Type, Enum> keyValuePair in ActiveStates)
|
||||
{
|
||||
if (StateGroupRegistry.GetTypeId(keyValuePair.Key) != switchContainer.SwitchGroupId) continue;
|
||||
currentStateValue = keyValuePair.Value;
|
||||
foundGroup = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundGroup)
|
||||
{
|
||||
Debug.LogWarning($"[AudioSystem] Switch Container {switchContainer.Id} 找不到 TypeId={switchContainer.SwitchGroupId} 对应的状态组。");
|
||||
return this.m_audioObjects.GetDefaultSwitchOrFallback(switchContainer);
|
||||
}
|
||||
|
||||
// 解析AudioObject对象
|
||||
AudioObject childContainer = this.m_audioObjects.GetMappingResult(switchContainer.Id, currentStateValue);
|
||||
return childContainer ?? this.m_audioObjects.GetDefaultSwitchOrFallback(switchContainer);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static class AudioConfigLoader
|
||||
@@ -194,9 +236,9 @@ namespace OCES.Audio
|
||||
|
||||
try
|
||||
{
|
||||
var config = new T();
|
||||
using var ms = new MemoryStream(File.ReadAllBytes(path));
|
||||
using var reader = new BinaryReader(ms);
|
||||
T config = new T();
|
||||
using MemoryStream ms = new(File.ReadAllBytes(path));
|
||||
using BinaryReader reader = new(ms);
|
||||
config.DeSerialize(reader);
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace OCES.Audio.Editor
|
||||
|
||||
public Dictionary<uint, int> ClipConcurrentCount = new();
|
||||
public List<ActiveSound> ActiveSounds = new();
|
||||
public Dictionary<Type, int> ActiveStates = new();
|
||||
public Dictionary<Type, Enum> ActiveStates = new();
|
||||
readonly StringBuilder m_stringBuilder = new();
|
||||
Text m_textComponent;
|
||||
|
||||
@@ -35,10 +35,9 @@ namespace OCES.Audio.Editor
|
||||
this.m_stringBuilder.Clear();
|
||||
|
||||
this.m_stringBuilder.AppendLine("Current States:");
|
||||
foreach (KeyValuePair<Type, int> activeState in this.ActiveStates)
|
||||
foreach (KeyValuePair<Type, Enum> activeState in this.ActiveStates)
|
||||
{
|
||||
string enumName = Enum.GetName(activeState.Key, activeState.Value) ?? activeState.Value.ToString();
|
||||
this.m_stringBuilder.AppendLine($"{activeState.Key.Name} is {enumName}");
|
||||
this.m_stringBuilder.AppendLine($"{activeState.Key.Name} is {activeState.Value}");
|
||||
}
|
||||
this.m_stringBuilder.AppendLine();
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace OCES.Audio
|
||||
Random = 0,
|
||||
Sequence,
|
||||
Blend,
|
||||
Switch,
|
||||
}
|
||||
|
||||
public enum BlendCrossFadeType
|
||||
@@ -84,4 +85,10 @@ namespace OCES.Audio
|
||||
|
||||
public partial class MusicPath : IPathEntry { }
|
||||
public partial class AmbiencePath : IPathEntry { }
|
||||
|
||||
public class SwitchEntry
|
||||
{
|
||||
public uint SwitchValue;
|
||||
public uint AudioObjectId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ namespace OCES.Audio
|
||||
/// </summary>
|
||||
public class MusicStateRouter
|
||||
{
|
||||
// key: StateGroup enum Type,value: 当前激活的 enum 整数值
|
||||
readonly Dictionary<Type, int> m_activeStates = new();
|
||||
|
||||
// key: StateGroup enum Type,value: 当前激活的 enum 值
|
||||
readonly Dictionary<Type, Enum> m_activeStates = new();
|
||||
|
||||
|
||||
readonly MusicPathConfig m_musicPaths;
|
||||
readonly AmbiencePathConfig m_ambiencePaths;
|
||||
@@ -18,6 +20,7 @@ namespace OCES.Audio
|
||||
// 上一次匹配到的 PathId,用于 Transition 表的 FromPathId 查询
|
||||
public uint LastMusicPathId { get; private set; }
|
||||
public uint LastAmbiencePathId { get; private set; }
|
||||
internal IReadOnlyDictionary<Type, Enum> ActiveStates { get { return this.m_activeStates; }}
|
||||
|
||||
public MusicStateRouter(MusicPathConfig musicPaths, AmbiencePathConfig ambiencePaths)
|
||||
{
|
||||
@@ -31,8 +34,8 @@ namespace OCES.Audio
|
||||
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);
|
||||
// Dictionary<Type, Enum> 天然保证同一 StateGroup 只保留最新值,直接覆盖即可
|
||||
this.m_activeStates[typeof(TEnum)] = state;
|
||||
|
||||
musicContainerId = MatchBestPath(this.m_musicPaths.MusicPathList(), out uint musicPathId);
|
||||
ambienceContainerId = MatchBestPath(this.m_ambiencePaths.AmbiencePathList(), out uint ambiencePathId);
|
||||
@@ -99,9 +102,9 @@ namespace OCES.Audio
|
||||
}
|
||||
|
||||
bool conditionMet = false;
|
||||
foreach (KeyValuePair<Type, int> kv in this.m_activeStates)
|
||||
foreach (KeyValuePair<Type, Enum> kv in this.m_activeStates)
|
||||
{
|
||||
if (StateGroupRegistry.GetTypeId(kv.Key) != typeId || kv.Value != stateValue)
|
||||
if (StateGroupRegistry.GetTypeId(kv.Key) != typeId || Convert.ToInt32(kv.Value) != stateValue)
|
||||
continue;
|
||||
conditionMet = true;
|
||||
break;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OCES.Audio
|
||||
@@ -16,6 +17,11 @@ namespace OCES.Audio
|
||||
// 记录上一次两个通道各自匹配到的 PathId,用于查 Transition 表
|
||||
uint m_lastMusicPathId;
|
||||
uint m_lastAmbiencePathId;
|
||||
|
||||
internal IReadOnlyDictionary<Type, Enum> ActiveStates
|
||||
{
|
||||
get { return this.m_stateRouter.ActiveStates; }
|
||||
}
|
||||
|
||||
public void Initialize(
|
||||
MusicSegmentConfig segments,
|
||||
|
||||
@@ -488,8 +488,6 @@ namespace OCES.Audio
|
||||
|
||||
DecrementClipCount(active.AudioObject.Id);
|
||||
this.m_activeSounds.Remove(active);
|
||||
// TryStopHaptic(active.AudioObject.Haptic);
|
||||
|
||||
this.m_pool.ReturnToPool(active.Source.gameObject);
|
||||
}
|
||||
|
||||
@@ -511,8 +509,6 @@ namespace OCES.Audio
|
||||
DecrementClipCount(active.AudioObject.Id);
|
||||
this.m_activeSounds.Remove(active);
|
||||
this.m_pool.ReturnToPool(active.Source.gameObject);
|
||||
|
||||
// TryStopHaptic(active.AudioObject.Haptic);
|
||||
}
|
||||
|
||||
static void TryStartHaptic(ActiveSound active)
|
||||
@@ -526,7 +522,6 @@ namespace OCES.Audio
|
||||
{
|
||||
HapticSystem.Instance.Stop(hapticId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user