using UnityEngine; using FMODUnity; using FMOD.Studio; public class FirstPersonOcclusion : MonoBehaviour { [Header("FMOD Event")] [SerializeField] private FMODUnity.EventReference SelectAudio; private EventInstance AudioOccluded; private EventDescription AudioDes; private StudioListener Listener; private PLAYBACK_STATE pb; [Header("Occlusion Options")] [SerializeField] [Range(0f, 10f)] private float SoundOcclusionWidening = 1f; [SerializeField] [Range(0f, 10f)] private float PlayerOcclusionWidening = 1f; [SerializeField] private LayerMask OcclusionLayer; private bool AudioIsVirtual; private float MaxDistance; private float ListenerDistance; private float lineCastHitCount = 0f; private Color colour; private void Start() { Debug.Log("--- Start Method ---"); // 1. Event Instance Creation AudioOccluded = RuntimeManager.CreateInstance(SelectAudio); Debug.Log($"Created FMOD Event Instance for: {SelectAudio.Path}"); // 2. Attaching Instance RuntimeManager.AttachInstanceToGameObject(AudioOccluded, gameObject); Debug.Log($"Attached FMOD Event Instance to GameObject: {gameObject.name}"); // 3. Starting Audio AudioOccluded.start(); Debug.Log("Started FMOD Event Instance."); // 4. Releasing Instance (This allows the event to self-manage its lifetime, which is fine) AudioOccluded.release(); Debug.Log("Released FMOD Event Instance."); // 5. Getting Event Description and Max Distance AudioDes = RuntimeManager.GetEventDescription(SelectAudio); AudioDes.getMinMaxDistance(out float minDistance, out MaxDistance); Debug.Log($"FMOD Event Min/Max Distance: {minDistance:F2} / {MaxDistance:F2}"); // 6. Finding Listener Listener = FindObjectOfType(); if (Listener != null) { Debug.Log($"Found FMOD StudioListener on GameObject: {Listener.gameObject.name}"); } else { Debug.LogError("FATAL: Could not find FMOD StudioListener in the scene!"); } Debug.Log("--- End Start Method ---"); } private void FixedUpdate() { // Debug.Log("--- FixedUpdate Method ---"); // Too frequent, only log conditions AudioOccluded.isVirtual(out AudioIsVirtual); AudioOccluded.getPlaybackState(out pb); ListenerDistance = Vector3.Distance(transform.position, Listener.transform.position); // 7. Check Occlusion Condition if (!AudioIsVirtual && pb == PLAYBACK_STATE.PLAYING && ListenerDistance <= MaxDistance) { Debug.Log($"Occlusion Check: Not Virtual, Playing, Distance {ListenerDistance:F2} <= Max {MaxDistance:F2}. Calling OccludeBetween."); OccludeBetween(transform.position, Listener.transform.position); } else { // Log reasons why occlusion is NOT being checked if (AudioIsVirtual) Debug.Log("Occlusion Skipped: Audio is Virtual."); if (pb != PLAYBACK_STATE.PLAYING) Debug.Log($"Occlusion Skipped: Playback State is not PLAYING. State: {pb}"); if (ListenerDistance > MaxDistance) Debug.Log($"Occlusion Skipped: Distance {ListenerDistance:F2} > Max Distance {MaxDistance:F2}."); } // 8. Reset Hit Count // Debug.Log($"Resetting lineCastHitCount from {lineCastHitCount:F0} to 0."); lineCastHitCount = 0f; } private void OccludeBetween(Vector3 sound, Vector3 listener) { Debug.Log("--- OccludeBetween Method ---"); // 9. Calculate Points (Log only a few to avoid clutter) Vector3 SoundLeft = CalculatePoint(sound, listener, SoundOcclusionWidening, true); Vector3 SoundRight = CalculatePoint(sound, listener, SoundOcclusionWidening, false); // Debug.Log($"Sound Positions: Center {sound}, Left {SoundLeft}, Right {SoundRight}"); Vector3 ListenerLeft = CalculatePoint(listener, sound, PlayerOcclusionWidening, true); Vector3 ListenerRight = CalculatePoint(listener, sound, PlayerOcclusionWidening, false); // Debug.Log($"Listener Positions: Center {listener}, Left {ListenerLeft}, Right {ListenerRight}"); Vector3 SoundAbove = new Vector3(sound.x, sound.y + SoundOcclusionWidening, sound.z); Vector3 SoundBelow = new Vector3(sound.x, sound.y - SoundOcclusionWidening, sound.z); Vector3 ListenerAbove = new Vector3(listener.x, listener.y + PlayerOcclusionWidening * 0.5f, listener.z); Vector3 ListenerBelow = new Vector3(listener.x, listener.y - PlayerOcclusionWidening * 0.5f, listener.z); // 10. Casting Lines (The line casts themselves will log hits) CastLine(SoundLeft, ListenerLeft); CastLine(SoundLeft, listener); CastLine(SoundLeft, ListenerRight); CastLine(sound, ListenerLeft); CastLine(sound, listener); CastLine(sound, ListenerRight); CastLine(SoundRight, ListenerLeft); CastLine(SoundRight, listener); CastLine(SoundRight, ListenerRight); CastLine(SoundAbove, ListenerAbove); CastLine(SoundBelow, ListenerBelow); if (PlayerOcclusionWidening == 0f || SoundOcclusionWidening == 0f) { colour = Color.blue; } else { colour = Color.green; } SetParameter(); Debug.Log("--- End OccludeBetween Method ---"); } private Vector3 CalculatePoint(Vector3 a, Vector3 b, float m, bool posOrneg) { // Debug.Log($"Calculating offset point for: {a} to {b} with magnitude {m} and posOrneg {posOrneg}"); float x; float z; // n is the 2D distance between a and b float n = Vector3.Distance(new Vector3(a.x, 0f, a.z), new Vector3(b.x, 0f, b.z)); float mn = (m / n); // Safety check for division by zero (if sound and listener are exactly on top of each other horizontally) if (n == 0f) { // If points are on the same XZ position, just return the point 'a' // Debug.LogWarning("CalculatePoint: Division by zero avoided. Sound and Listener are at the same XZ position."); return a; } if (posOrneg) { x = a.x + (mn * (a.z - b.z)); z = a.z - (mn * (a.x - b.x)); } else { x = a.x - (mn * (a.z - b.z)); z = a.z + (mn * (a.x - b.x)); } return new Vector3(x, a.y, z); } private void CastLine(Vector3 Start, Vector3 End) { RaycastHit hit; // 11. Raycast result bool isHit = Physics.Linecast(Start, End, out hit, OcclusionLayer); if (isHit) { lineCastHitCount++; Debug.Log($"Linecast HIT! Hit Count: {lineCastHitCount:F0}/11. Object hit: {hit.collider.name}."); Debug.DrawLine(Start, End, Color.red); } else { Debug.DrawLine(Start, End, colour); } } private void SetParameter() { float occlusionValue = lineCastHitCount / 11; // 11 is the total number of line casts // 12. Final Parameter Value Debug.Log($"Setting FMOD Parameter 'Occlusion' to: {occlusionValue:F2} (Hits: {lineCastHitCount:F0}/11)"); AudioOccluded.setParameterByName("Occlusion", occlusionValue); } }