Files
ichni_Official/Assets/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioSubmodule.cs
SoulliesOfficial 7580c4d87c 大更
2026-03-14 03:13:10 -04:00

150 lines
5.7 KiB
C#

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<string> generalJudgeAudioList;
public List<string> perfectAudioList;
public List<string> goodAudioList;
public List<string> badAudioList;
public List<string> missAudioList;
public List<string> 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<string>() { defaultAudio };
perfectAudioList = new List<string>();
goodAudioList = new List<string>();
badAudioList = new List<string>();
missAudioList = new List<string>();
holdStartAudioList = new List<string>();
if (!HaveSameSubmodule)
{
this.note.NoteAudioSubmodule = this;
}
InitializeAudio();
}
public NoteAudioSubmodule(NoteBase attachedGameElement, List<string> generalJudgeAudioList,
List<string> perfectAudioList, List<string> goodAudioList, List<string> badAudioList,
List<string> missAudioList, List<string> holdStartAudioList) : base(attachedGameElement)
{
this.generalJudgeAudioList = generalJudgeAudioList ?? new List<string>();
this.perfectAudioList = perfectAudioList ?? new List<string>();
this.goodAudioList = goodAudioList ?? new List<string>();
this.badAudioList = badAudioList ?? new List<string>();
this.missAudioList = missAudioList ?? new List<string>();
this.holdStartAudioList = holdStartAudioList ?? new List<string>();
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<string> audioNames)
{
if (audioNames == null || audioNames.Count == 0) return new uint[0];
List<uint> ids = new List<uint>(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
}