Skip to Content
DocsVoicereactVoice Listeners

Voice Listeners

VoiceListener makes enemies, NPCs, and game objects react to player voice based on distance and volume.

Basic Setup

Add a VoiceListener to any GameObject that should hear the player:

// 1. Select enemy GameObject // 2. Add Component > VoiceListener // 3. Set Base Hearing Range (e.g., 10 meters) // 4. Hook OnPlayerHeard event

The listener automatically:

  • Finds the player’s VoiceReactController
  • Calculates distance to player
  • Determines if voice is audible based on volume and range
  • Fires events when player is heard/lost

Hearing Ranges

VoiceListener uses volume-based dynamic hearing ranges:

[SerializeField] private VoiceListener listener; void Start() { // Set base hearing range (when player is at "Loud" volume) listener.baseHearingRange = 10f; }

Range Multipliers

Control how far different volume levels can be heard:

listener.whisperMultiplier = 0.2f; // Whisper: 2m (10 * 0.2) listener.normalMultiplier = 0.5f; // Normal: 5m (10 * 0.5) listener.loudMultiplier = 1.0f; // Loud: 10m (10 * 1.0) listener.shoutMultiplier = 2.0f; // Shout: 20m (10 * 2.0)

The effective hearing range is calculated automatically based on player’s current volume:

  • Louder speech = larger hearing range
  • Quieter speech = smaller hearing range
  • Smooth interpolation between volume levels

Listener Properties

Current Hearing State

// Is the listener currently hearing the player? if (listener.CanHearPlayer) { Debug.Log("Currently hearing player!"); }

Distance Information

// Get distance to player float distance = listener.DistanceToPlayer; // Get effective hearing range (based on current player volume) float range = listener.EffectiveHearingRange; // Get last known player position Vector3 lastPos = listener.LastKnownPlayerPosition;

Volume Information

// Get the decibel level when player was heard float db = listener.HeardDecibelLevel;

Events

VoiceListener provides UnityEvents for integration with both code and Inspector.

OnPlayerHeard

Fires when player becomes audible:

void Start() { listener.OnPlayerHeard.AddListener(OnHeardPlayer); } void OnHeardPlayer(Vector3 position, float decibelLevel) { Debug.Log($"Heard player at {position} with {decibelLevel} dB"); // React to player voice InvestigatePosition(position); }

Event Parameters:

  • position - Player’s 3D position when heard
  • decibelLevel - Volume in decibels (-60 to 0 dB)

OnPlayerLost

Fires when player becomes inaudible:

void Start() { listener.OnPlayerLost.AddListener(OnLostPlayer); } void OnLostPlayer() { Debug.Log("Lost the player"); // Return to normal behavior ReturnToPatrol(); }

OnDecibelLevelChanged

Fires when volume changes significantly while hearing player:

void Start() { listener.OnDecibelLevelChanged.AddListener(OnVolumeChanged); } void OnVolumeChanged(float newDecibelLevel) { Debug.Log($"Player volume changed to {newDecibelLevel} dB"); // React to volume changes if (newDecibelLevel > -15f) { // Player started shouting - become more aggressive IncreaseAggression(); } }

Line of Sight

Require line of sight to hear player (sound blocked by walls):

[SerializeField] private VoiceListener listener; void Start() { listener.requireLineOfSight = true; listener.occlusionLayers = LayerMask.GetMask("Walls", "Obstacles"); }

When enabled:

  • Raycasts from listener to player
  • If raycast hits occlusion layer, player is not heard
  • Perfect for realistic sound propagation

Multiple Listeners

VoiceReactManager efficiently updates all listeners in your scene.

Get All Listeners

// Get all registered listeners var allListeners = VoiceReactManager.Instance.GetAllListeners(); Debug.Log($"Total listeners: {allListeners.Count}");

Get Listeners Who Can Hear

// Get only listeners currently hearing the player var hearingListeners = VoiceReactManager.Instance.GetListenersWhoCanHear(); foreach (var listener in hearingListeners) { Debug.Log($"{listener.gameObject.name} can hear the player"); }

From VoiceReactController

[SerializeField] private VoiceReactController playerVoice; void Update() { if (playerVoice.IsPlayerSpeaking) { // Get all enemies that can hear this player var hearingEnemies = playerVoice.GetListenersWhoCanHear(); if (hearingEnemies.Count > 0) { Debug.Log($"{hearingEnemies.Count} enemies can hear you!"); } } }

Performance Optimization

VoiceReactManager automatically optimizes listener updates:

Update Frequency

Control how often listeners check if they can hear:

var manager = VoiceReactManager.Instance; // Update 30 times per second (default) manager.updateFrequency = 30f; // Lower frequency for better performance manager.updateFrequency = 15f; // Higher frequency for more responsive detection manager.updateFrequency = 60f;

Update Only While Speaking

Skip updates when player is silent:

// Only update when player is actively speaking (default: true) manager.onlyUpdateWhileSpeaking = true;

This optimization:

  • Skips all listener updates when player is silent
  • Reduces CPU usage significantly
  • Recommended for most games

Common Patterns

Alert Meter

Build tension by tracking how many enemies hear you:

using UnityEngine; using Peek.VoiceReact; public class AlertSystem : MonoBehaviour { [SerializeField] private VoiceReactController playerVoice; [SerializeField] private float maxAlertLevel = 100f; private float currentAlert = 0f; void Update() { int hearingEnemies = playerVoice.GetListenersWhoCanHear().Count; if (hearingEnemies > 0) { // Increase alert based on number of enemies currentAlert += hearingEnemies * Time.deltaTime * 10f; } else { // Decrease alert when not being heard currentAlert -= Time.deltaTime * 5f; } currentAlert = Mathf.Clamp(currentAlert, 0f, maxAlertLevel); if (currentAlert >= maxAlertLevel) { TriggerGameOver(); } } }

Closest Listener

Find the closest enemy that can hear you:

VoiceListener FindClosestListener() { var hearingListeners = VoiceReactManager.Instance.GetListenersWhoCanHear(); if (hearingListeners.Count == 0) return null; VoiceListener closest = hearingListeners[0]; float closestDist = closest.DistanceToPlayer; foreach (var listener in hearingListeners) { if (listener.DistanceToPlayer < closestDist) { closest = listener; closestDist = listener.DistanceToPlayer; } } return closest; }

Volume-Based AI States

Different reactions based on volume:

void OnHeardPlayer(Vector3 position, float decibelLevel) { if (decibelLevel > -10f) { // Shout - immediate attack aiState = AIState.Attack; agent.SetDestination(position); } else if (decibelLevel > -20f) { // Loud - investigate quickly aiState = AIState.Investigate; agent.speed = 5f; agent.SetDestination(position); } else if (decibelLevel > -30f) { // Normal - cautious investigation aiState = AIState.Investigate; agent.speed = 3f; agent.SetDestination(position); } else { // Whisper - look around but don't move aiState = AIState.Alert; LookTowards(position); } }

Hearing Memory

Remember player position even after they go silent:

public class EnemyHearingMemory : MonoBehaviour { private VoiceListener listener; private Vector3 lastKnownPosition; private float memoryDuration = 10f; private float memoryTimer = 0f; void Start() { listener = GetComponent<VoiceListener>(); listener.OnPlayerHeard.AddListener(OnHeardPlayer); listener.OnPlayerLost.AddListener(OnLostPlayer); } void Update() { if (memoryTimer > 0) { memoryTimer -= Time.deltaTime; if (memoryTimer <= 0) { // Forgot player position OnMemoryFaded(); } } } void OnHeardPlayer(Vector3 position, float db) { lastKnownPosition = position; memoryTimer = memoryDuration; } void OnLostPlayer() { // Still remember position for memoryDuration seconds Debug.Log("Lost player, but I remember where they were..."); } void OnMemoryFaded() { Debug.Log("Forgot where player was"); ReturnToPatrol(); } }

Debug Visualization

VoiceListener includes Scene view gizmos:

[SerializeField] private bool showDebugGizmos = true;

Gizmos show:

  • Current effective range (red when hearing, gray when not)
  • Base hearing range (white wireframe)
  • Line to player (when currently hearing)
  • Volume range spheres (when selected):
    • Blue = Whisper range
    • Green = Normal range
    • Orange = Loud range
    • Red = Shout range

Remote Player Voice (Networking)

Process voice from remote players over network:

// Called when receiving voice data from network public void OnRemotePlayerVoiceReceived(float normalizedVolume, float distance) { // Let listener process remote player voice listener.ProcessRemotePlayerVoice(normalizedVolume, distance); }

See Networking for full multiplayer integration.

Next Steps

Last updated on