Empieza gratis →

Cómo implementar Voice Chat en Unity con Vivox (proximidad + radio)

L
Lautaro Bravo de la Serna
6 de abril de 202612 min de lectura
Cómo implementar Voice Chat en Unity con Vivox (proximidad + radio)

Lo que vas a lograr

  • Entender qué es Vivox y cómo se integra con Unity Gaming Services
  • Implementar voz por proximidad 3D que se atenúa con la distancia
  • Agregar un canal de radio global push-to-talk estilo Phasmophobia
  • Escribir el VoiceChatManager completo listo para tu juego
  • Configurar parámetros de audio 3D, mute y extras

Si jugaste Phasmophobia sabés de qué hablo: por defecto, solo te escuchan los jugadores que están cerca. Si querés hablar con alguien lejos, tenés que usar la radio, pero el fantasma también te puede escuchar.

En esta guía vas a implementar exactamente eso: voz por proximidad que se atenúa con la distancia y un canal de radio global push-to-talk. Vivox se encarga de todo lo complicado del audio. Vos solo le decís quién está dónde.


🎙️

¿Qué es Vivox?

🔊

Vivox = el motor de voz de Unity

Vivox es el servicio de voice chat de Unity Gaming Services. Se ocupa de capturar el micrófono, comprimir el audio, transmitirlo por internet, posicionarlo en 3D y suprimir el ruido. No necesitás enviar audio por RPCs ni NetworkVariables porque Vivox tiene sus propios servidores.

Tu proyecto solo le dice "este jugador está en esta posición" y Vivox se encarga de que cada uno escuche a los demás con el volumen correcto según la distancia.

Precio: gratis hasta 5,000 usuarios concurrentes por mes. Para desarrollo, cursos y juegos indie, no vas a pagar nada.

📡

Arquitectura de canales

Para replicar el sistema de Phasmophobia necesitás dos canales de Vivox simultáneos:

CanalTipoPropósito
ProximidadPositional (3D)Voz que se atenúa con la distancia
RadioGroup (no-positional)Radio global, todos escuchan a todos

El jugador siempre está conectado a ambos canales. La diferencia es a cuál transmite:

Por defecto: proximidad

Transmite solo al canal 3D. Los demás te escuchan si están cerca.

Manteniendo V: radio

Transmite a ambos canales. Todos te escuchan sin importar la distancia.


1

Instalar Vivox

  1. 1En Unity: Package Manager > Unity Registry → buscar "Vivox" → instalar com.unity.services.vivox (v16.9+)
  2. 2En Edit > Project Settings > Services → vincular tu proyecto a Unity Dashboard (si no lo hiciste ya con Lobby/Relay)
  3. 3En el Unity Dashboard, habilitar Vivox en tu proyecto
Si ya tenés Authentication y Multiplayer Services instalados (por Lobby/Relay), Vivox se integra directamente con esos mismos servicios. No necesitás configurar nada adicional.
2

El código del VoiceChatManager

Creá un script VoiceChatManager.cs y ponelo en el mismo GameObject que el NetworkManager (o uno que persista entre escenas):

VoiceChatManager.cs
using Unity.Services.Authentication;
using Unity.Services.Vivox;
using UnityEngine;

public class VoiceChatManager : MonoBehaviour
{
    public static VoiceChatManager Instance { get; private set; }

    [Header("Audio 3D")]
    [SerializeField] private int audibleDistance = 30;
    [SerializeField] private int conversationalDistance = 5;
    [SerializeField] private float fadeIntensity = 1.0f;
    [SerializeField] private AudioFadeModel fadeModel = AudioFadeModel.InverseByDistance;

    [Header("Controles")]
    [SerializeField] private KeyCode radioKey = KeyCode.V;

    private bool _isConnected;
    private bool _isTransmittingRadio;
    private string _proximityChannel;
    private string _radioChannel;

    private void Awake()
    {
        if (Instance != null)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
    }

    // Llamar despues de que UGS ya este inicializado y autenticado
    public async void StartVoiceChat(string lobbyId)
    {
        _proximityChannel = $"proximity-{lobbyId}";
        _radioChannel = $"radio-{lobbyId}";

        await VivoxService.Instance.InitializeAsync();
        await VivoxService.Instance.LoginAsync(new LoginOptions
        {
            DisplayName = AuthenticationService.Instance.PlayerId
        });

        // Canal de proximidad (3D positional)
        var props = new Channel3DProperties(
            audibleDistance,
            conversationalDistance,
            fadeIntensity,
            fadeModel
        );

        await VivoxService.Instance.JoinPositionalChannelAsync(
            _proximityChannel,
            ChatCapability.AudioOnly,
            props
        );

        // Canal de radio (global)
        await VivoxService.Instance.JoinGroupChannelAsync(
            _radioChannel,
            ChatCapability.AudioOnly
        );

        // Por defecto, transmitir solo al canal de proximidad
        await VivoxService.Instance.SetChannelTransmissionModeAsync(
            TransmissionMode.Single,
            _proximityChannel
        );

        _isConnected = true;
    }

    private void Update()
    {
        if (!_isConnected) return;
        HandleRadioInput();
    }

    private async void HandleRadioInput()
    {
        if (Input.GetKeyDown(radioKey) && !_isTransmittingRadio)
        {
            _isTransmittingRadio = true;
            await VivoxService.Instance.SetChannelTransmissionModeAsync(
                TransmissionMode.All,
                null
            );
        }

        if (Input.GetKeyUp(radioKey) && _isTransmittingRadio)
        {
            _isTransmittingRadio = false;
            await VivoxService.Instance.SetChannelTransmissionModeAsync(
                TransmissionMode.Single,
                _proximityChannel
            );
        }
    }

    public async void StopVoiceChat()
    {
        if (!_isConnected) return;
        _isConnected = false;
        await VivoxService.Instance.LeaveAllChannelsAsync();
        await VivoxService.Instance.LogoutAsync();
    }

    private void OnDestroy()
    {
        StopVoiceChat();
    }
}

¿Qué hace cada parte?

  • StartVoiceChat: Inicializa Vivox, hace login, se une a ambos canales (proximidad 3D + radio global) y configura la transmisión por defecto a solo proximidad.
  • HandleRadioInput: Mientras mantenés la tecla V, cambia la transmisión a todos los canales. Al soltar, vuelve a solo proximidad.
  • StopVoiceChat: Sale de todos los canales y desloguea de Vivox.
Usamos el lobbyId como sufijo en los nombres de los canales. Así cada partida tiene sus propios canales de voz y jugadores de partidas diferentes no se escuchan entre sí.
3

Actualizar la posición 3D

Para que el audio posicional funcione, Vivox necesita saber dónde está cada jugador. Hay que enviarle la posición periódicamente, no cada frame. Con 5 veces por segundo alcanza.

Agregá esto en tu script del jugador (donde tengas referencia al transform del jugador local):

ShooterPlayer.cs (fragmento)
private float _nextVivoxUpdate;

// Dentro de Update(), solo para el jugador local:
if (IsOwner && Time.time >= _nextVivoxUpdate)
{
    _nextVivoxUpdate = Time.time + 0.2f; // 5 veces por segundo
    VivoxService.Instance.Set3DPosition(gameObject, "proximity-{lobbyId}");
}
📍

¿Qué hace Set3DPosition?

Toma automáticamente la posición y rotación del GameObject para calcular desde dónde "habla" y desde dónde "escucha" el jugador. Vivox usa esa información para calcular el volumen en tiempo real.

4

Conectar con el flujo del juego

En tu ConnectionManager o donde manejes el flujo Host/Client, después de que la conexión de red esté establecida:

C#
// Despues de conectar al juego
VoiceChatManager.Instance.StartVoiceChat(lobbyId);

// Cuando el jugador vuelve al menu
VoiceChatManager.Instance.StopVoiceChat();
Llamá a StartVoiceChat() después de SignInAnonymouslyAsync(). Vivox necesita que Authentication esté activo para funcionar.
5

Configurar los parámetros de audio 3D

Los valores de Channel3DProperties controlan cómo se atenúa la voz con la distancia:

|<-- conversationalDistance -->|<-------- fade zone -------->|

     (volumen 100%)        (se atenúa gradualmente)   silencio

                                                          ^ audibleDistance

ParámetroValorQué hace
audibleDistance30Rango máximo donde se escucha algo
conversationalDistance5Dentro de 5 unidades se escucha al 100%
fadeIntensity1.0Atenuación normal (subir a 2.0+ para caída más rápida)
fadeModelInverseByDistanceAtenuación natural como el sonido real

Modelos de atenuación

  • InverseByDistance: caída natural tipo 1/distancia. Realista.
  • LinearByDistance: caída uniforme. Más predecible para el jugador.
  • ExponentialByDistance: caída agresiva. Se escucha fuerte de cerca y casi nada a distancia media.
Ajustá según el tamaño de tu mapa. Si tu mapa es chico, bajá audibleDistance a 15-20.

🎛️

Extras opcionales

Indicador visual de radio

Para que el jugador sepa cuándo está transmitiendo por radio, mostrá un icono en la UI:

C#
// En tu script de HUD
[SerializeField] private GameObject radioIndicator;

private void Update()
{
    radioIndicator.SetActive(Input.GetKey(KeyCode.V));
}

Mute y control de volumen

C#
// Mutear/desmutear micrófono
VivoxService.Instance.MuteInputDevice();
VivoxService.Instance.UnmuteInputDevice();

// Mutear/desmutear parlantes
VivoxService.Instance.MuteOutputDevice();
VivoxService.Instance.UnmuteOutputDevice();

// Ajustar volumen (0-100)
VivoxService.Instance.InputDeviceVolume = 50;   // mic
VivoxService.Instance.OutputDeviceVolume = 70;   // lo que escuchás

Saber quién está hablando

Para mostrar un icono de "hablando" encima de cada jugador:

C#
VivoxService.Instance.ParticipantAddedToChannel += (participant) =>
{
    // participant.PlayerId  -> ID del jugador
    // participant.IsSpeaking -> bool en tiempo real
    // participant.AudioEnergy -> float 0-1, volumen actual
};
Podés hacer un poll de participant.IsSpeaking en Update para mostrar/ocultar un icono de voz sobre el jugador correspondiente.

Push-to-talk para todo (no solo radio)

Si querés que el mic solo esté activo cuando apretás una tecla (como Discord PTT):

C#
// Arrancar muteado despues del login:
VivoxService.Instance.MuteInputDevice();

// En Update:
if (Input.GetKeyDown(KeyCode.T))
    VivoxService.Instance.UnmuteInputDevice();

if (Input.GetKeyUp(KeyCode.T))
    VivoxService.Instance.MuteInputDevice();

Modo "muerto" (no puede hablar)

Si un jugador muere y no debería poder hablar (como en Phasmophobia):

C#
// Cuando muere: sacarlo del canal de proximidad
await VivoxService.Instance.LeaveChannelAsync(proximityChannel);

// Cuando respawnea: reconectarlo
await VivoxService.Instance.JoinPositionalChannelAsync(proximityChannel, ...);

⚠️

Problemas comunes

ProblemaCausa probableSolución
No se escucha nadaVivox no inicializadoVerificá que StartVoiceChat() se llame después de SignInAnonymouslyAsync()
Se escuchan todos igualNo se actualiza Set3DPositionVerificá que solo el Owner lo llama y que el channel name coincide
Audio cortadoSet3DPosition cada frameLimitá a 5 updates por segundo (cada 0.2s)
Canal no encontradoNombre diferente entre Host y ClientUsá el mismo lobbyId para generar los nombres
Radio no funcionaTransmissionMode no cambiaVerificá que SetChannelTransmissionModeAsync se completa sin error
Eco / feedbackParlantes captan el micVivox tiene supresión de eco por defecto, pero auriculares ayudan

🔄

Flujo completo resumido

1. Jugador conecta al juego (Host o Client)

2. UGS ya inicializado (Authentication, Lobby, Relay)

3. VoiceChatManager.StartVoiceChat(lobbyId)

├── Vivox Login

├── Join canal "proximity-{lobbyId}" (3D positional)

├── Join canal "radio-{lobbyId}" (group)

└── Transmisión default: solo proximidad

4. En cada Update (5 veces/seg):

└── Set3DPosition del jugador local

5. Jugador habla:

├── Sin apretar nada → solo proximidad

└── Manteniendo V → todos lo escuchan

6. Jugador se desconecta:

└── VoiceChatManager.StopVoiceChat()


🚀

¿Y ahora qué?

Con esto tu juego ya tiene voice chat con audio posicional y radio. Es de esas features que cambian completamente cómo se siente jugar: no es lo mismo leer un chat que escuchar a tu compañero gritando que hay un enemigo atrás tuyo.

Si querés seguir y armar el flujo completo de un juego multiplayer (Relay, Lobbies, voice chat, optimización de latencia), en el curso premium lo cubrimos paso a paso:

Ver el curso completo de Multiplayer

6 módulos, 48 lecciones. Desde la base hasta dedicated servers, pasando por Relay, Lobbies, voice chat y optimización.

Ver el curso completo

Preguntas Frecuentes

¿Vivox es gratis?

Sí, hasta 5,000 usuarios concurrentes por mes. Para desarrollo, cursos y juegos indie es más que suficiente.

¿Puedo usar Vivox sin Netcode for GameObjects?

Sí. Vivox tiene sus propios servidores de audio, independientes del networking de tu juego. Podés usarlo con cualquier solución multijugador (NGO, Mirror, Fusion, etc.).

¿Funciona en móviles?

Sí. Vivox soporta Windows, macOS, Linux, iOS, Android y consolas. El SDK maneja los permisos de micrófono automáticamente en cada plataforma.

¿Necesito un servidor dedicado para el voice chat?

No. Vivox usa sus propios servidores en la nube. Tu proyecto solo necesita indicar a qué canales conectarse y las posiciones de los jugadores.

¿Puedo tener chat de texto además de voz?

Sí. Vivox maneja texto con SendChannelTextMessageAsync() y ChannelMessageReceived. Podés usarlo en el mismo canal de voz.

¿Qué pasa si un jugador no tiene micrófono?

Puede escuchar a los demás sin problemas. Vivox maneja input y output de audio por separado.

Si tenés dudas sobre la implementación, preguntá en el Discord de Codearte.

Más contenido gratuito de Unity y desarrollo de juegos en el canal de YouTube.

Tags:#unity#vivox#voice-chat#multiplayer#netcode#gamedev#multijugador#audio-3d
🎮

¿Quieres crear tus propios juegos?

Aprende a programar videojuegos desde cero con nuestros cursos en Unity. Más de 3,000 estudiantes ya empezaron su camino.

Ver cursos gratuitos