#if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms. /******************************************************************************* The content of this file includes portions of the proprietary AUDIOKINETIC Wwise Technology released in source code form as part of the game integration package. The content of this file may not be used without valid licenses to the AUDIOKINETIC Wwise Technology. Note that the use of the game engine is subject to the Unity(R) Terms of Service at https://unity3d.com/legal/terms-of-service License Usage Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use this file in accordance with the end user license agreement provided with the software or, alternatively, in accordance with the terms contained in a written agreement between you and Audiokinetic Inc. Copyright (c) 2025 Audiokinetic Inc. *******************************************************************************/ [UnityEngine.AddComponentMenu("Wwise/Spatial Audio/AkRoomPortal")] [UnityEngine.RequireComponent(typeof(UnityEngine.BoxCollider))] [UnityEngine.DisallowMultipleComponent] /// @brief An AkRoomPortal can connect two AkRoom components together. /// @details public class AkRoomPortal : AkTriggerHandler { /// AkRoomPortals can only connect a maximum of 2 rooms. public const int MAX_ROOMS_PER_PORTAL = 2; public enum State { Closed, Open } public State initialState = State.Closed; private bool active = true; public bool portalActive { get { return active; } set { active = value; portalNeedsUpdate = true; AkRoomManager.RegisterPortalUpdate(this); } } public System.Collections.Generic.List closePortalTriggerList = new System.Collections.Generic.List(); private ulong frontRoomID { get { return IsRoomActive(frontRoom) ? frontRoom.GetID() : AkRoom.INVALID_ROOM_ID; } } private ulong backRoomID { get { return IsRoomActive(backRoom) ? backRoom.GetID() : AkRoom.INVALID_ROOM_ID; } } /// The front and back rooms connected by the portal. /// The first room is on the negative side of the portal(opposite to the direction of the local Z axis) /// The second room is on the positive side of the portal. [UnityEngine.SerializeField] private AkRoom[] rooms = new AkRoom[MAX_ROOMS_PER_PORTAL]; /// The list of rooms sorted by priority in front and in the back of the portal private AkRoom.PriorityList[] roomList = { new AkRoom.PriorityList(), new AkRoom.PriorityList() }; public AkRoom GetRoom(int index) { return rooms[index]; } public AkRoom frontRoom { get { return rooms[1]; } } public AkRoom backRoom { get { return rooms[0]; } } public bool isSetInWwise() { return portalSet; } private AkTransform portalTransform; private UnityEngine.BoxCollider portalCollider; private bool portalSet = false; private bool portalNeedsUpdate = false; private UnityEngine.Vector3 previousPosition; private UnityEngine.Vector3 previousScale; private UnityEngine.Quaternion previousRotation; private void SetRoomPortal() { if (!AkUnitySoundEngine.IsInitialized()) { return; } if (!isActiveAndEnabled) { return; } if (IsValid) { portalTransform.Set(portalCollider.bounds.center, transform.forward, transform.up); var extentVector = UnityEngine.Vector3.Scale(portalCollider.size, transform.lossyScale) / 2; // in case extent is negative, get the absolute value AkExtent extent = new AkExtent( UnityEngine.Mathf.Abs(extentVector.x), UnityEngine.Mathf.Abs(extentVector.y), UnityEngine.Mathf.Abs(extentVector.z)); AkUnitySoundEngine.SetRoomPortal(GetID(), frontRoomID, backRoomID, portalTransform, extent, active, name); portalSet = true; portalNeedsUpdate = false; } else { UnityEngine.Debug.LogWarning(name + " Portal placement is invalid. The portal is not set in the Spatial Audio engine. The front and back Rooms of the Portal cannot be the same or have a ReverbZone-parent relationship."); if (portalSet) { AkUnitySoundEngine.RemovePortal(GetID()); portalSet = false; } } } public void UpdateRoomPortal() { bool roomsChanged = UpdateRooms(); if (roomsChanged || !portalSet || portalNeedsUpdate) { SetRoomPortal(); } } public bool Overlaps(AkRoom room) { FindOverlappingRooms(roomList); for (int i = 0; i < MAX_ROOMS_PER_PORTAL; ++i) { if (roomList[i].Contains(room)) { return true; } } return false; } public bool IsValid { get { // portal is valid if its front and back rooms are different bool isPortalValid = frontRoomID != backRoomID; // portal is valid if its front and back room don't have a ReverbZone-parent relationship if (isPortalValid && frontRoom && frontRoom.IsAReverbZoneInWwise) { isPortalValid = backRoomID != frontRoom.ParentRoomID; } if (isPortalValid && backRoom && backRoom.IsAReverbZoneInWwise) { isPortalValid = frontRoomID != backRoom.ParentRoomID; } #if UNITY_EDITOR // check all reverb zone components if (isPortalValid) { AkReverbZone[] reverbZoneComponents = UnityEngine.Resources.FindObjectsOfTypeAll(); for (uint i = 0; i < reverbZoneComponents.Length; ++i) { if (reverbZoneComponents[i].isActiveAndEnabled && reverbZoneComponents[i].ReverbZone) { ulong reverbZoneID = reverbZoneComponents[i].ReverbZone.GetID(); ulong parentRoomID = AkRoom.INVALID_ROOM_ID; if (reverbZoneComponents[i].ParentRoom != null) { parentRoomID = reverbZoneComponents[i].ParentRoom.GetID(); } isPortalValid = !(frontRoomID == reverbZoneID && backRoomID == parentRoomID); isPortalValid = isPortalValid && !(backRoomID == reverbZoneID && frontRoomID == parentRoomID); } } } #endif return isPortalValid; } } /// Access the portal's ID public ulong GetID() { return (ulong)GetInstanceID(); } protected override void Awake() { portalCollider = GetComponent(); portalCollider.isTrigger = true; portalTransform = new AkTransform(); // set portal in it's initial state portalActive = initialState != State.Closed; RegisterTriggers(closePortalTriggerList, ClosePortal); // init update condition previousPosition = transform.position; previousScale = transform.lossyScale; previousRotation = transform.rotation; base.Awake(); } protected override void Start() { base.Start(); //Call the ClosePortal function if registered to the Start Trigger if (closePortalTriggerList.Contains(START_TRIGGER_ID)) { ClosePortal(null); } } /// Opens the portal on trigger event public override void HandleEvent(UnityEngine.GameObject in_gameObject) { Open(); } /// Closes the portal on trigger event public void ClosePortal(UnityEngine.GameObject in_gameObject) { Close(); } protected override void OnDestroy() { base.OnDestroy(); UnregisterTriggers(closePortalTriggerList, ClosePortal); } public override void OnEnable() { AkRoomManager.RegisterPortal(this); base.OnEnable(); } private void OnDisable() { AkRoomManager.UnregisterPortal(this); if (portalSet) { AkUnitySoundEngine.RemovePortal(GetID()); } portalSet = false; } private void Update() { if (previousPosition != transform.position || previousScale != transform.lossyScale || previousRotation != transform.rotation) { portalNeedsUpdate = true; AkRoomManager.RegisterPortalUpdate(this); previousPosition = transform.position; previousScale = transform.lossyScale; previousRotation = transform.rotation; } } private bool IsRoomActive(AkRoom in_room) { return in_room != null && in_room.isActiveAndEnabled; } public void Open() { portalActive = true; } public void Close() { portalActive = false; } public void FindOverlappingRooms(AkRoom.PriorityList[] roomList) { var portalCollider = gameObject.GetComponent(); if (portalCollider == null) { return; } // compute halfExtents and divide the local z extent by 2 var halfExtentZ = portalCollider.size.z / 2; // move the center backward FillRoomList(UnityEngine.Vector3.forward * -halfExtentZ, roomList[0]); // move the center forward FillRoomList(UnityEngine.Vector3.forward * halfExtentZ, roomList[1]); } private void FillRoomList(UnityEngine.Vector3 position, AkRoom.PriorityList list) { list.Clear(); position = transform.TransformPoint(position); var colliders = UnityEngine.Physics.OverlapSphere(position, 0, -1, UnityEngine.QueryTriggerInteraction.Collide); foreach (var collider in colliders) { var room = collider.gameObject.GetComponent(); if (room != null && !list.Contains(room)) { list.Add(room); } } } public bool UpdateRooms() { FindOverlappingRooms(roomList); bool wasUpdated = false; for (var i = 0; i < MAX_ROOMS_PER_PORTAL; ++i) { var room = roomList[i].GetHighestPriorityActiveAndEnabledRoom(); if (room != rooms[i]) { wasUpdated = true; } rooms[i] = room; } return wasUpdated; } #if UNITY_EDITOR private void OnDrawGizmos() { if (!enabled) { return; } UnityEngine.Gizmos.matrix = transform.localToWorldMatrix; var centreOffset = UnityEngine.Vector3.zero; var sizeMultiplier = UnityEngine.Vector3.one; var collider = GetComponent(); if (collider) { centreOffset = collider.center; sizeMultiplier = collider.size; } // color faces var faceCenterPos = new UnityEngine.Vector3[6]; faceCenterPos[0] = UnityEngine.Vector3.Scale(new UnityEngine.Vector3(0.5f, 0.0f, 0.0f), sizeMultiplier); faceCenterPos[1] = UnityEngine.Vector3.Scale(new UnityEngine.Vector3(0.0f, 0.5f, 0.0f), sizeMultiplier); faceCenterPos[2] = UnityEngine.Vector3.Scale(new UnityEngine.Vector3(-0.5f, 0.0f, 0.0f), sizeMultiplier); faceCenterPos[3] = UnityEngine.Vector3.Scale(new UnityEngine.Vector3(0.0f, -0.5f, 0.0f), sizeMultiplier); faceCenterPos[4] = UnityEngine.Vector3.Scale(new UnityEngine.Vector3(0.0f, 0.0f, 0.5f), sizeMultiplier); faceCenterPos[5] = UnityEngine.Vector3.Scale(new UnityEngine.Vector3(0.0f, 0.0f, -0.5f), sizeMultiplier); var faceSize = new UnityEngine.Vector3[6]; faceSize[0] = new UnityEngine.Vector3(0, 1, 1); faceSize[1] = new UnityEngine.Vector3(1, 0, 1); faceSize[2] = faceSize[0]; faceSize[3] = faceSize[1]; faceSize[4] = new UnityEngine.Vector3(1, 1, 0); faceSize[5] = faceSize[4]; if (IsValid) { UnityEngine.Gizmos.color = new UnityEngine.Color32(255, 204, 0, 100); } else { UnityEngine.Gizmos.color = new UnityEngine.Color32(255, 0, 0, 100); } for (var i = 0; i < 4; i++) { UnityEngine.Gizmos.DrawCube(faceCenterPos[i] + centreOffset, UnityEngine.Vector3.Scale(faceSize[i], sizeMultiplier)); } if (!portalActive) { UnityEngine.Gizmos.DrawCube(faceCenterPos[4] + centreOffset, UnityEngine.Vector3.Scale(faceSize[4], sizeMultiplier)); UnityEngine.Gizmos.DrawCube(faceCenterPos[5] + centreOffset, UnityEngine.Vector3.Scale(faceSize[5], sizeMultiplier)); } // draw line in the center of the portal var CornerCenterPos = faceCenterPos; CornerCenterPos[0].y += 0.5f * sizeMultiplier.y; CornerCenterPos[1].x -= 0.5f * sizeMultiplier.x; CornerCenterPos[2].y -= 0.5f * sizeMultiplier.y; CornerCenterPos[3].x += 0.5f * sizeMultiplier.x; UnityEngine.Gizmos.color = UnityEngine.Color.green; for (var i = 0; i < 4; i++) { UnityEngine.Gizmos.DrawLine(CornerCenterPos[i] + centreOffset, CornerCenterPos[(i + 1) % 4] + centreOffset); } } #endif #region Obsolete [System.Obsolete(AkUnitySoundEngine.Deprecation_2019_2_0)] public void SetRoom(int in_roomIndex, AkRoom in_room) { UnityEngine.Debug.LogFormat("SetRoom is deprecated. Highest priority, active and enabled room will be automatically chosen. Make sure room priorities and game object placements are correct."); } [System.Obsolete(AkUnitySoundEngine.Deprecation_2019_2_0)] public void SetFrontRoom(AkRoom room) { UnityEngine.Debug.LogFormat("SetFrontRoom is deprecated. Highest priority, active and enabled room will be automatically chosen. Make sure room priorities and game object placements are correct."); } [System.Obsolete(AkUnitySoundEngine.Deprecation_2019_2_0)] public void SetBackRoom(AkRoom room) { UnityEngine.Debug.LogFormat("SetBackRoom is deprecated. Highest priority, active and enabled room will be automatically chosen. Make sure room priorities and game object placements are correct."); } [System.Obsolete(AkUnitySoundEngine.Deprecation_2019_2_0)] public void UpdateSoundEngineRoomIDs() { UpdateRoomPortal(); } [System.Obsolete(AkUnitySoundEngine.Deprecation_2019_2_0)] public void UpdateOverlappingRooms() { UpdateRooms(); } #endregion } #endif // #if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.