using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Profiling; using Wwise.Wooduan.Components; using Wwise.Wooduan.Core; using Wwise.Wooduan.Tools; public class PooledAkGameObj : AkGameObj { private float _creationTime, _eventFadeStartTime, _eventFinishTime; public bool IsLocked { get; private set; } public string EventName { get; internal set; } public Action OnEventFinished; private void OnEnable() { autoUpdatePosition = false; } internal void Activate() { _creationTime = Time.time; _eventFinishTime = _eventFadeStartTime = 0f; IsLocked = false; Register(); } internal void FinishEvent() { if (!IsLocked) _eventFinishTime = Time.time; OnEventFinished?.Invoke(); OnEventFinished = null; } public void ManualRecycle(float delayTime = 0f, float fadeOutTime = 0f) { _eventFinishTime = Time.time + delayTime; if (fadeOutTime > 0) { _eventFadeStartTime = _eventFinishTime; _eventFinishTime += fadeOutTime; } IsLocked = true; } public void LockFromAutoRecycle() { IsLocked = true; } internal bool CanRecycle() { if (_eventFinishTime > 0f) { if (Time.time > _eventFinishTime) return true; if (_eventFadeStartTime > 0f && Time.time > _eventFadeStartTime) { AudioManager.StopEvent(EventName, gameObject, _eventFinishTime - _eventFadeStartTime); _eventFadeStartTime = 0f; } } var timeSinceCreation = Time.time - _creationTime; if (timeSinceCreation > 10f && EmitterManager.GetActiveVoiceCount(gameObject) == 0) return true; return !IsLocked && timeSinceCreation > 60f; } } public partial class AkGameObj : UnityEngine.MonoBehaviour { //Set default to false since we use rooms instead [Tooltip("If this emitter will send to auxbus based on AkEnvironment")] public bool isEnvironmentAware = false; [Tooltip("If this emitter will send to auxbus based on AkRoom")] public bool isRoomAware = true; [Tooltip("If this emitter will automatically update its position data")] public bool autoUpdatePosition = true; [Tooltip("If this emitter will update its position less frequent when far away")] public bool dynamicUpdateInterval = true; [Tooltip("If this emitter will respond to spot reflectors in the map")] public bool useSpotReflector = false; [Tooltip("If this emitter will cast rays to calculate occlusion and obstruction")] public bool enableOccObsRTPC = false; #region LifeCycle public bool IsRegistered { get; set; } protected virtual void Awake() { // If the object was marked as static, don't update its position to save cycles. if (!isStaticObject) m_posData = new AkGameObjPositionData(); else autoUpdatePosition = false; // Cache the bounds to avoid calls to GetComponent() m_Collider = GetComponent(); //Register a Game Object in the sound engine, with its name. if (Register() == AKRESULT.AK_Success) { if (isEnvironmentAware) { m_envData = new AkGameObjEnvironmentData(); if (m_Collider) m_envData.AddAkEnvironment(m_Collider, m_Collider); m_envData.UpdateAuxSend(gameObject, transform.position); } m_listeners.Init(this); } } internal AKRESULT Register() { if (!AkSoundEngine.IsInitialized()) return AKRESULT.AK_Fail; if (IsRegistered) return AKRESULT.AK_Success; IsRegistered = true; return AkSoundEngine.RegisterGameObj(gameObject, name); } internal AKRESULT Unregister() { if (!AkSoundEngine.IsInitialized()) return AKRESULT.AK_Fail; if (!IsRegistered) return AKRESULT.AK_Success; CurrentSpace = null; IsRegistered = false; EmitterManager.UnregisterGameSync(gameObject); AkSoundEngine.StopAll(gameObject); var result = AkSoundEngine.UnregisterGameObj(gameObject); return result; } private void OnEnable() { EmitterManager.RegisterDefaultEmitter(this); if (useSpotReflector) AkSpotReflector.SetSpotReflectors(this); InitPosition(); } private void OnDestroy() { // We can't do the code in OnDestroy if the gameObj is unregistered, so do it now. var eventHandlers = gameObject.GetComponents(); foreach (var handler in eventHandlers) { if (handler.triggerList.Contains(AkTriggerHandler.DESTROY_TRIGGER_ID)) handler.DoDestroy(); } Unregister(); } #endregion LifeCycle #region P1_P3 public GameObject GameObject => gameObject; public AkGameObjListenerList Listeners => m_listeners; internal bool IsOwnedByListener { get; set; } public ulong GetID() { return AkSoundEngine.GetAkGameObjectID(gameObject); } public bool IsSelfEmitter { get => _selfEmitter == this; set { if (value) SelfEmitter = this; else if (_selfEmitter == this) SelfEmitter = null; } } public static AkGameObj SelfEmitter { get => _selfEmitter; private set { if (_selfEmitter == value) return; if (_selfEmitter) AudioManager.SetSwitch("P1_P3", "P3", _selfEmitter.gameObject); if (value) AudioManager.SetSwitch("P1_P3", "P1", value.gameObject); _selfEmitter = value; } } private static AkGameObj _selfEmitter; #endregion P1_P3 #region SpatialAudio private float _timeFromLastSpatialAudioUpdate; private float _spatialAudioRefreshInterval = 0.2f; private AkSpace _currentSpace; internal AkSpace CurrentSpace { get => _currentSpace; set { if (value) value.OnEmitterEntered(this); if (_currentSpace) _currentSpace.OnEmitterExited(this); AudioManager.SetRTPCValue("Outdoor", value && !value.IsOutdoor ? 0 : 1, gameObject); _currentSpace = value; } } internal AkSpatialAudioRoom CurrentRoom { get { if (IsSelfEmitter) { var listener = ListenerManager.GetListener(); if (listener) return listener.CurrentRoom; } return _currentSpace ? _currentSpace.Room : null; } } public bool IsOutdoor => !CurrentSpace || CurrentSpace.IsOutdoor; public int GetFloor() { return _currentSpace ? _currentSpace.GetFloor(GetPosition()) : AkSpace.AK_INVALID_FLOOR; } #endregion SpatialAudio #region UpdatePosition private int _frameCounter, _positionUpdateIntervalInFrames; // position moved since last update private Vector3 _deltaPosition; public void InitPosition() { SetPosition(); CurrentSpace = null; if (isRoomAware) SpatialAudioManager.UpdateSpatialAudioRoom(this); else AkSoundEngine.SetGameObjectInRoom(gameObject, AkRoom.INVALID_ROOM_ID); } public void UpdatePosition() { Profiler.BeginSample("AkGameObj.UpdatePosition"); _timeFromLastSpatialAudioUpdate += Time.deltaTime; if (!IsSelfEmitter && autoUpdatePosition) SetPosition(); // ignore room when emitter is not moving or too far away if (_timeFromLastSpatialAudioUpdate >= _spatialAudioRefreshInterval) { if (_deltaPosition != Vector3.zero && SpatialAudioManager.RoomEnabled) { if (m_envData != null) m_envData.UpdateAuxSend(gameObject, transform.position); if (isRoomAware && !IsSelfEmitter) SpatialAudioManager.UpdateSpatialAudioRoom(this); } if (!IsSelfEmitter && enableOccObsRTPC) { SpatialAudioManager.UpdateOcclusionObstruction(this, out var occlusion, out var obstruction); AudioManager.SetRTPCValue("Obstruction", obstruction, gameObject, 0.1f); var floorDifference = occlusion > 0 ? SpatialAudioManager.CalculateFloorDifference(this, ListenerManager.GetListener()) : 0; AudioManager.SetRTPCValue("Occlusion", floorDifference == 0 ? occlusion : 0, gameObject, 0.1f); AudioManager.SetRTPCValue("Floor_Difference", floorDifference, gameObject); } else { AudioManager.SetRTPCValue("Obstruction", 0, gameObject); AudioManager.SetRTPCValue("Occlusion", 0, gameObject); AudioManager.SetRTPCValue("Floor_Difference", 0, gameObject); } _timeFromLastSpatialAudioUpdate = 0; } Profiler.EndSample(); } public void SetPosition() { if (IsSelfEmitter) return; var position = GetPosition(); var forward = GetForward(); var up = GetUpward(); if (m_posData != null) { _deltaPosition = position - m_posData.position; m_posData.position = position; m_posData.forward = forward; m_posData.up = up; } AkSoundEngine.SetObjectPosition(gameObject, position, forward, up); return; } public virtual bool ShouldUpdateThisFrame() { if (IsSelfEmitter) return true; if (!autoUpdatePosition || m_posData == null) return false; if (!dynamicUpdateInterval) return true; _frameCounter++; if (_frameCounter < _positionUpdateIntervalInFrames) return false; if (EmitterManager.GetActiveVoiceCount(gameObject) == 0) return false; _positionUpdateIntervalInFrames = EmitterManager.CalculateUpdateFrequency(m_posData.position); _spatialAudioRefreshInterval = 0.1f + _positionUpdateIntervalInFrames * 0.1f; _frameCounter = 0; return true; } #endregion UpdatePosition }