2026-02-13 09:22:11 -05:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using AK.Wwise;
|
|
|
|
|
|
using Lean.Pool;
|
|
|
|
|
|
using Sirenix.OdinInspector;
|
|
|
|
|
|
using SLSUtilities.General;
|
|
|
|
|
|
|
|
|
|
|
|
namespace SLSUtilities.WwiseAssistance
|
|
|
|
|
|
{
|
|
|
|
|
|
public partial class AudioManager : Singleton<AudioManager>
|
|
|
|
|
|
{
|
|
|
|
|
|
[Title("Settings")]
|
|
|
|
|
|
[Required]
|
|
|
|
|
|
public GameObject audioPoint;
|
|
|
|
|
|
public List<Bank> soundBanks;
|
|
|
|
|
|
|
|
|
|
|
|
[Title("Subsystems")]
|
|
|
|
|
|
public BackgroundMusicManager backgroundMusicManager;
|
|
|
|
|
|
|
2026-05-26 10:48:49 -04:00
|
|
|
|
private static Dictionary<string, uint> _trackedPlayingIDs = new Dictionary<string, uint>();
|
2026-02-13 09:22:11 -05:00
|
|
|
|
|
|
|
|
|
|
protected override void Awake()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.Awake();
|
|
|
|
|
|
soundBanks.ForEach(bank => bank.Load());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public partial class AudioManager
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 【核心方法】在指定位置播放 3D 音效
|
|
|
|
|
|
/// 使用方法: AudioManager.Instance.PlayEvent(AK.EVENTS.EXPLOSION, position);
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="eventID">AK.EVENTS 中的静态 ID</param>
|
|
|
|
|
|
/// <param name="position">世界坐标</param>
|
|
|
|
|
|
/// <returns>返回 WwiseEmitter,以便需要时手动 Stop</returns>
|
|
|
|
|
|
public static AudioPoint Post(uint eventID, Vector3 position = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (eventID == 0) return null;
|
|
|
|
|
|
|
|
|
|
|
|
AudioPoint point = LeanPool.Spawn(instance.audioPoint, position, Quaternion.identity).GetComponent<AudioPoint>();
|
|
|
|
|
|
point.Play(eventID);
|
|
|
|
|
|
|
|
|
|
|
|
return point;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static uint Post(uint eventID, GameObject attachedObject)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (eventID == 0 || attachedObject == null) return AkUnitySoundEngine.AK_INVALID_PLAYING_ID;
|
|
|
|
|
|
return AkUnitySoundEngine.PostEvent(eventID, attachedObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static AudioPoint Post(string eventName, Vector3 position)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Post(AkUnitySoundEngine.GetIDFromString(eventName), position);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void Post(string eventName, GameObject attachedObject)
|
|
|
|
|
|
{
|
|
|
|
|
|
Post(AkUnitySoundEngine.GetIDFromString(eventName), attachedObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void Stop(uint playingID, int fadeOutMs = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (playingID == AkUnitySoundEngine.AK_INVALID_PLAYING_ID) return;
|
|
|
|
|
|
|
|
|
|
|
|
AkUnitySoundEngine.ExecuteActionOnPlayingID(
|
|
|
|
|
|
AkActionOnEventType.AkActionOnEventType_Stop,
|
|
|
|
|
|
playingID, fadeOutMs,
|
|
|
|
|
|
AkCurveInterpolation.AkCurveInterpolation_Linear
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void StopAll()
|
|
|
|
|
|
{
|
|
|
|
|
|
AkUnitySoundEngine.StopAll();
|
2026-05-26 10:48:49 -04:00
|
|
|
|
_trackedPlayingIDs.Clear();
|
2026-02-13 09:22:11 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public partial class AudioManager
|
|
|
|
|
|
{
|
|
|
|
|
|
private static void RegisterTracking(string trackingKey, uint playingID)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (playingID != AkUnitySoundEngine.AK_INVALID_PLAYING_ID)
|
|
|
|
|
|
{
|
2026-05-26 10:48:49 -04:00
|
|
|
|
_trackedPlayingIDs[trackingKey] = playingID;
|
2026-02-13 09:22:11 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void Post(string trackingKey, uint eventID, Vector3 position = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
Stop(trackingKey);
|
|
|
|
|
|
AudioPoint point = Post(eventID, position);
|
|
|
|
|
|
uint playingID = point.eventID;
|
|
|
|
|
|
RegisterTracking(trackingKey, playingID);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void Post(string trackingKey, string eventName, Vector3 position = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
Post(trackingKey, AkUnitySoundEngine.GetIDFromString(eventName), position);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void Post(string trackingKey, uint eventID, GameObject attachedObject)
|
|
|
|
|
|
{
|
|
|
|
|
|
Stop(trackingKey);
|
|
|
|
|
|
uint playingID = Post(eventID, attachedObject);
|
|
|
|
|
|
RegisterTracking(trackingKey, playingID);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void Post(string trackingKey, string eventName, GameObject attachedObject)
|
|
|
|
|
|
{
|
|
|
|
|
|
Post(trackingKey, AkUnitySoundEngine.GetIDFromString(eventName), attachedObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void Pause(string trackingKey, int fadeOutMs = 0)
|
|
|
|
|
|
{
|
2026-05-26 10:48:49 -04:00
|
|
|
|
if (_trackedPlayingIDs.TryGetValue(trackingKey, out uint playingID))
|
2026-02-13 09:22:11 -05:00
|
|
|
|
{
|
|
|
|
|
|
AkUnitySoundEngine.ExecuteActionOnPlayingID(
|
|
|
|
|
|
AkActionOnEventType.AkActionOnEventType_Pause,
|
|
|
|
|
|
playingID, fadeOutMs,
|
|
|
|
|
|
AkCurveInterpolation.AkCurveInterpolation_Linear
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void PauseAllTrackedEvents(int fadeOutMs = 0)
|
|
|
|
|
|
{
|
2026-05-26 10:48:49 -04:00
|
|
|
|
foreach (var playingID in _trackedPlayingIDs.Values)
|
2026-02-13 09:22:11 -05:00
|
|
|
|
{
|
|
|
|
|
|
AkUnitySoundEngine.ExecuteActionOnPlayingID(
|
|
|
|
|
|
AkActionOnEventType.AkActionOnEventType_Pause,
|
|
|
|
|
|
playingID, fadeOutMs,
|
|
|
|
|
|
AkCurveInterpolation.AkCurveInterpolation_Linear
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- 【新增】核心控制:继续 ---
|
|
|
|
|
|
public static void Resume(string trackingKey, int fadeInMs = 0)
|
|
|
|
|
|
{
|
2026-05-26 10:48:49 -04:00
|
|
|
|
if (_trackedPlayingIDs.TryGetValue(trackingKey, out uint playingID))
|
2026-02-13 09:22:11 -05:00
|
|
|
|
{
|
|
|
|
|
|
AkUnitySoundEngine.ExecuteActionOnPlayingID(
|
|
|
|
|
|
AkActionOnEventType.AkActionOnEventType_Resume,
|
|
|
|
|
|
playingID, fadeInMs,
|
|
|
|
|
|
AkCurveInterpolation.AkCurveInterpolation_Linear
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void ResumeAllTrackedEvents(int fadeInMs = 0)
|
|
|
|
|
|
{
|
2026-05-26 10:48:49 -04:00
|
|
|
|
foreach (var playingID in _trackedPlayingIDs.Values)
|
2026-02-13 09:22:11 -05:00
|
|
|
|
{
|
|
|
|
|
|
AkUnitySoundEngine.ExecuteActionOnPlayingID(
|
|
|
|
|
|
AkActionOnEventType.AkActionOnEventType_Resume,
|
|
|
|
|
|
playingID, fadeInMs,
|
|
|
|
|
|
AkCurveInterpolation.AkCurveInterpolation_Linear
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 【新增】通过 Key 停止特定音效
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static void Stop(string trackingKey, int fadeOutMs = 0)
|
|
|
|
|
|
{
|
2026-05-26 10:48:49 -04:00
|
|
|
|
if (_trackedPlayingIDs.TryGetValue(trackingKey, out uint playingID))
|
2026-02-13 09:22:11 -05:00
|
|
|
|
{
|
|
|
|
|
|
Stop(playingID, fadeOutMs);
|
2026-05-26 10:48:49 -04:00
|
|
|
|
_trackedPlayingIDs.Remove(trackingKey);
|
2026-02-13 09:22:11 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 【新增】停止所有被追踪的循环音效 (常用于切换场景前)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static void StopAllTrackedEvents(int fadeOutMs = 0)
|
|
|
|
|
|
{
|
2026-05-26 10:48:49 -04:00
|
|
|
|
foreach (var playingID in _trackedPlayingIDs.Values)
|
2026-02-13 09:22:11 -05:00
|
|
|
|
{
|
|
|
|
|
|
Stop(playingID, fadeOutMs);
|
|
|
|
|
|
}
|
2026-05-26 10:48:49 -04:00
|
|
|
|
_trackedPlayingIDs.Clear();
|
2026-02-13 09:22:11 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public partial class AudioManager
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置全局参数 (RTPC)
|
|
|
|
|
|
/// 例如: "MasterVolume", "MusicVolume", "PlayerHealth"
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetRTPC(string rtpcName, float value)
|
|
|
|
|
|
{
|
|
|
|
|
|
AkUnitySoundEngine.SetRTPCValue(rtpcName, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置状态 (State)
|
|
|
|
|
|
/// 例如: Group="MusicState", State="Combat" / "Explore"
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetState(string stateGroup, string stateName)
|
|
|
|
|
|
{
|
|
|
|
|
|
AkUnitySoundEngine.SetState(stateGroup, stateName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置全局开关 (Switch) - 通常用于特定的全局对象
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetGlobalSwitch(string switchGroup, string switchName)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 对于全局Switch,通常传一个全局GameObject,或者使用 Wwise 的 Global Scope
|
|
|
|
|
|
AkUnitySoundEngine.SetSwitch(switchGroup, switchName, gameObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|