Files
Cielonos/Assets/Scripts/MainGame/Effects/Feedbacks/Actions/Audio/PostAudioAction.cs
SoulliesOfficial 9a9e48f8a5
2026-06-27 12:52:03 -04:00

144 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using SLSUtilities.Feedback;
using SLSUtilities.WwiseAssistance;
using Lean.Pool;
namespace Cielonos.MainGame.Effects.Feedback
{
/// <summary>
/// 音频反馈动作,在 Feedback 轨道上播放 Wwise 音频事件。
/// 支持跟随执行者Play Attached或在 3D 空间中指定位置播放,并支持打断时平滑淡出。
/// </summary>
[Serializable]
[FeedbackActionColor(0.2f, 0.6f, 0.9f)]
public class PostAudioAction : FeedbackActionBase
{
public override string DisplayName => "Post Audio";
[Required]
[Tooltip("要播放的 Wwise 音频事件")]
public AK.Wwise.Event wwiseEvent;
[LabelText("跟随执行者播放")]
[Tooltip("如果为 true音效将绑定在执行者 GameObject 上移动;如果为 false将在生成时的坐标固定播放 3D 音效。")]
public bool playAttached = true;
[LabelText("被打断时停止")]
[Tooltip("如果为 true反馈轨被打断如受击、格挡此音效会立刻停止/淡出。")]
public bool stopOnInterrupt = true;
[ShowIf("stopOnInterrupt")]
[LabelText("淡出时间 (ms)")]
[Tooltip("被打断停止时的淡出时长(毫秒)。")]
public int fadeOutMs = 200;
// 运行时状态记录,使用 Dictionary 保证并发/多角色播放时的实例安全
private readonly Dictionary<FeedbackPlayer, uint> _playingIDs = new Dictionary<FeedbackPlayer, uint>();
private readonly Dictionary<FeedbackPlayer, GameObject> _spawnedSoundObjects = new Dictionary<FeedbackPlayer, GameObject>();
public override void OnStart(FeedbackContext context)
{
if (wwiseEvent == null) return;
GameObject ownerObj = context.owner != null ? context.owner.gameObject : null;
uint playingID = 0;
if (playAttached && ownerObj != null)
{
// 绑定到执行者播放
playingID = wwiseEvent.Post(ownerObj);
if (playingID != 0)
{
_playingIDs[context.player] = playingID;
}
}
else
{
// 3D 空间固定位置播放
Vector3 position = context.owner != null ? context.owner.position : Vector3.zero;
if (AudioManager.Instance != null && AudioManager.Instance.audioPoint != null)
{
GameObject soundObj = LeanPool.Spawn(AudioManager.Instance.audioPoint, position, Quaternion.identity);
_spawnedSoundObjects[context.player] = soundObj;
playingID = wwiseEvent.Post(soundObj, (uint)AkCallbackType.AK_EndOfEvent, AutoDespawnCallback, context.player);
if (playingID != 0)
{
_playingIDs[context.player] = playingID;
soundObj.name = $"PostAudioAction-{wwiseEvent.Name}-Event{playingID}";
}
else
{
// 播放失败,立即回收对象
LeanPool.Despawn(soundObj);
_spawnedSoundObjects.Remove(context.player);
}
}
}
}
public override void OnInterrupt(FeedbackContext context)
{
if (stopOnInterrupt)
{
if (_playingIDs.TryGetValue(context.player, out uint playingID))
{
if (playingID != 0 && playingID != AkUnitySoundEngine.AK_INVALID_PLAYING_ID)
{
AudioManager.Stop(playingID, fadeOutMs);
}
}
}
Cleanup(context);
}
public override void OnEnd(FeedbackContext context)
{
Cleanup(context);
}
private void Cleanup(FeedbackContext context)
{
_playingIDs.Remove(context.player);
_spawnedSoundObjects.Remove(context.player);
}
/// <summary>
/// 空间音频的 Wwise 回调,在播放结束时自动回收临时音频点。
/// </summary>
private void AutoDespawnCallback(object in_cookie, AkCallbackType in_type, AkCallbackInfo in_info)
{
if (in_type == AkCallbackType.AK_EndOfEvent)
{
FeedbackPlayer player = in_cookie as FeedbackPlayer;
if (player != null)
{
if (_spawnedSoundObjects.TryGetValue(player, out GameObject soundObj))
{
if (soundObj != null)
{
LeanPool.Despawn(soundObj);
}
_spawnedSoundObjects.Remove(player);
}
_playingIDs.Remove(player);
}
}
}
public override bool Validate(out string error)
{
if (wwiseEvent == null)
{
error = "Wwise Event is not assigned.";
return false;
}
error = null;
return true;
}
}
}