using System; using System.Collections.Generic; using UnityEngine; using Wwise.Wooduan.Core; namespace Wwise.Wooduan.Components { [RequireComponent(typeof(BoxCollider))] public class AkBuilding : AkSpace, ICullableAudioObject { [Tooltip("For special room shapes, cast a ray up to determine if it's inside")] public float rayCastHeight; [Tooltip("If this building will be culled as a whole or by individual rooms")] public CullMode cullMode = CullMode.Grouped; [Tooltip("If listener is more distant than this distance, building will be disabled")] public int cullDistance = 30; internal override bool IsOutdoor => true; private List _rooms = new List(); private List _roofs = new List(); protected override void Init() { SpatialAudioManager.RegisterBuilding(this); } protected override void Dispose() { SpatialAudioManager.UnregisterBuilding(this); } internal void RegisterRoom(AkRoom room) { if (!_rooms.Contains(room)) _rooms.Add(room); } internal void UnregisterRoom(AkRoom room) { if (_rooms.Contains(room)) _rooms.Remove(room); } internal void RegisterRoof(AkRoof Roof) { if (!_roofs.Contains(Roof)) _roofs.Add(Roof); } internal void UnregisterRoof(AkRoof Roof) { if (_roofs.Contains(Roof)) _roofs.Remove(Roof); } private bool IsPositionIndoor(Vector3 position) { if (rayCastHeight == 0) return true; var result = Physics.Raycast(position, Vector3.up, rayCastHeight, ~SpatialAudioManager.OcclusionObstructionLayer); return result; } internal override AkSpace FindSpaceAtPosition(Vector3 position) { if (!isActiveAndEnabled || IsCulled) return null; if (!IsPositionWithinSpace(position)) return null; AkSpace foundSpace = this; // find room for indoor if (IsPositionIndoor(position)) { var highestPriority = -1; foreach (var room in _rooms) { var space = room.FindSpaceAtPosition(position); if (space && room.priority > highestPriority) { foundSpace = space; highestPriority = room.priority; } } if (foundSpace != this) return foundSpace; } // find roof for outdoor foreach (var roof in _roofs) { var space = roof.FindSpaceAtPosition(position); if (space) return space; } return foundSpace; } #region Portal internal AkRoomPortal FindNearestPortalOpenedToOutdoor(Vector3 position, out float distance) { distance = 9999f; AkRoomPortal nearestPortal = null; foreach (var room in _rooms) { if (!room.isActiveAndEnabled) continue; var portal = room.FindNearestPortalOpenedToOutdoor(position, out var newDistance); if (portal && newDistance < distance) { distance = newDistance; nearestPortal = portal; if (distance == 0f) break; } } return nearestPortal; } internal AkRoomPortal FindPortalAtPosition(Vector3 position, PortalType portalType) { foreach (var room in _rooms) { var portal = room.FindPortalAtPosition(position, portalType); if (portal) return portal; } return null; } #endregion Portal #region ICullableAudioObject public bool IsCulled { get; set; } public void TryCull(Vector3 listenerPosition) { if (cullDistance <= 0f) return; switch (cullMode) { case CullMode.Grouped: var distance = GetDistanceToPosition(listenerPosition); if (distance > cullDistance * 1.1f) OnCull(); else if (distance < cullDistance * 0.9f) OnReveal(); break; case CullMode.Individual: if (!SpatialAudioManager.CullByEmitter) { foreach (var room in _rooms) { room.TryCull(listenerPosition); } } break; case CullMode.None: break; } } public void OnCull() { if (IsCulled) return; IsCulled = true; if (!SpatialAudioManager.CullByEmitter) { foreach (var room in _rooms) { room.OnCull(); } } } public void OnReveal() { if (!IsCulled) return; IsCulled = false; if (!SpatialAudioManager.CullByEmitter) { foreach (var room in _rooms) { room.OnReveal(); } } } #endregion ICullableAudioObject internal override void Refresh(bool active) { foreach (var room in _rooms) { room.Refresh(active); } } public override bool IsValid() { return transform.childCount > 0; } } }