using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Ichni.RhythmGame.Beatmap; using SLSUtilities.WwiseAssistance; namespace Ichni.RhythmGame { public partial class NoteAudioSubmodule : SubmoduleBase { #region [暴露属性字段] Audio Settings public List generalJudgeAudioList; public List perfectAudioList; public List goodAudioList; public List badAudioList; public List missAudioList; public List holdStartAudioList; #endregion #region [运行时缓存变量] GC-Free Wwise IDs // --- 高性能无 GC 的 Wwise 预存 ID 数组 --- // 我们利用基础类型数组而不是 List 再次挤压最后一点遍历性能。 private uint[] _generalJudgeAudioIds; private uint[] _perfectAudioIds; private uint[] _goodAudioIds; private uint[] _badAudioIds; private uint[] _missAudioIds; private uint[] _holdStartAudioIds; private NoteBase note => attachedGameElement as NoteBase; #endregion #region [生命周期] Initialization public NoteAudioSubmodule(NoteBase attachedGameElement, string defaultAudio) : base(attachedGameElement) { generalJudgeAudioList = new List() { defaultAudio }; perfectAudioList = new List(); goodAudioList = new List(); badAudioList = new List(); missAudioList = new List(); holdStartAudioList = new List(); if (!HaveSameSubmodule) { this.note.NoteAudioSubmodule = this; } InitializeAudio(); } public NoteAudioSubmodule(NoteBase attachedGameElement, List generalJudgeAudioList, List perfectAudioList, List goodAudioList, List badAudioList, List missAudioList, List holdStartAudioList) : base(attachedGameElement) { this.generalJudgeAudioList = generalJudgeAudioList ?? new List(); this.perfectAudioList = perfectAudioList ?? new List(); this.goodAudioList = goodAudioList ?? new List(); this.badAudioList = badAudioList ?? new List(); this.missAudioList = missAudioList ?? new List(); this.holdStartAudioList = holdStartAudioList ?? new List(); if (!HaveSameSubmodule) { this.note.NoteAudioSubmodule = this; } InitializeAudio(); } public void InitializeAudio() { // 通过反射,只在生成(或池化取出)时进行一次大写转换与常数查询,彻底摒弃字符串带来的运行时哈希。 _generalJudgeAudioIds = ParseWwiseEventIDs(generalJudgeAudioList); _perfectAudioIds = ParseWwiseEventIDs(perfectAudioList); _goodAudioIds = ParseWwiseEventIDs(goodAudioList); _badAudioIds = ParseWwiseEventIDs(badAudioList); _missAudioIds = ParseWwiseEventIDs(missAudioList); _holdStartAudioIds = ParseWwiseEventIDs(holdStartAudioList); } private uint[] ParseWwiseEventIDs(List audioNames) { if (audioNames == null || audioNames.Count == 0) return new uint[0]; List ids = new List(audioNames.Count); Type eventsType = typeof(AK.EVENTS); for (int i = 0; i < audioNames.Count; i++) { if (string.IsNullOrEmpty(audioNames[i])) continue; string upperName = audioNames[i].ToUpper(); FieldInfo field = eventsType.GetField(upperName, BindingFlags.Public | BindingFlags.Static); if (field != null) { ids.Add((uint)field.GetValue(null)); } else { UnityEngine.Debug.LogWarning($"[Wwise 注意] 在 AK.EVENTS 中找不到对应的大写事件名称: {upperName}"); } } return ids.ToArray(); } #endregion } #region [播放控制] Audio Playback Methods public partial class NoteAudioSubmodule { public void PlayHoldStartAudio() { PlayAudioInternal(_holdStartAudioIds); } public void PlayGeneralJudgeAudios() { PlayAudioInternal(_generalJudgeAudioIds); } public void PlayNoteJudgeAudios(NoteBase.NoteJudgeType judgeType) { switch (judgeType) { case NoteBase.NoteJudgeType.Perfect: PlayAudioInternal(_perfectAudioIds); break; case NoteBase.NoteJudgeType.Good: PlayAudioInternal(_goodAudioIds); break; case NoteBase.NoteJudgeType.Bad: PlayAudioInternal(_badAudioIds); break; case NoteBase.NoteJudgeType.Miss: PlayAudioInternal(_missAudioIds); break; } } private void PlayAudioInternal(uint[] audioIds) { if (audioIds == null) return; for (int i = 0; i < audioIds.Length; i++) { // 最高性能播放:绕过 AudioManager 那些空物体创建,直接挂载到 AudioManager 的单例底层进行原生播放。 AkSoundEngine.PostEvent(audioIds[i], AudioManager.Instance.gameObject); } } } #endregion #region [Beatmap] Executing and Reversing #endregion }