> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/all-out-docs/docs-es/conceptos-centrales-del-motor/sound-effects.md).

# Sonido y respuesta háptica

Reproduce efectos de sonido y retroalimentación háptica local.

Usa la API SFX para reproducir efectos de sonido cortos (clics de la interfaz, pasos, impactos) y audio simple en bucle (bucles ambientales, música).

{% hint style="warning" %}
Actualmente solo **codificados en PCM `.wav`** se admiten archivos. Si tienes un `.wav` que no se importe/reproduzca, conviértelo a PCM usando Audacity (o una herramienta similar).
{% endhint %}

### Inicio rápido (reproducir un sonido)

```go
click := get_asset(SFX_Asset, "sfx/click.wav");

desc := SFX.default_sfx_desc();
SFX.play(click, desc);
```

### Referencia de la API SFX

```go
SFX_Asset :: class : Asset {}

SFX_Channel :: enum {
    SFX;
    MUSIC;
}

SFX_Desc :: struct {
    specific_to_player: Player;
    positional:       bool;
    position:         v2 #read_only;
    delay:            float;
    volume:           float;
    speed:            float;
    volume_perturb:   float;
    speed_perturb:    float;
    range_multiplier: float;
    loop_timeout:     float;
    entity_to_follow: u64;
    loop:             bool;
    channel:          SFX_Channel;
    unique_id:        u64;

    set_position :: method(p: v2);
}

SFX :: struct {
    play              :: proc(asset: SFX_Asset, desc: SFX_Desc) -> u64;
    stop              :: proc(id: u64);
    fade_out_and_stop :: proc(id: u64, fade_time: float);
    default_sfx_desc :: proc() -> SFX_Desc;
}
```

### Obtener un `SFX_Asset`

Los efectos de sonido viven en la carpeta `res` de tu juego (a menudo bajo `res/sfx/`).

* Arrastra sonidos desde el **Catálogo de recursos** (el editor los descarga en `res/`)
* O añade tu propio `.wav` archivo en `res/` desde tu máquina local

Luego refiérete a ellos por su ruta:

```go
pickup := get_asset(SFX_Asset, "sfx/pickup.wav");
```

{% hint style="warning" %}
Comprueba las rutas de los recursos durante el desarrollo. Un recurso faltante produce un handle nulo; `SFX.play` registra una advertencia y devuelve `0`.
{% endhint %}

{% hint style="info" %}
Algunos recursos de plataforma integrados usan rutas `$AO/...` . Por ejemplo: `get_asset(SFX_Asset, "$AO/sfx/FUI Hologram Ping Tone Echoed.wav")`.
{% endhint %}

### Sonidos posicionales (tipo 3D)

Por defecto, SFX se reproduce como un sonido “2D” no posicional. Para hacerlo posicional, establece una posición:

```go
play_hit_sfx :: proc(world_pos: v2) {
    hit := get_asset(SFX_Asset, "sfx/hit.wav");

    desc := SFX.default_sfx_desc();
    desc.set_position(world_pos);
    desc.range_multiplier = 1.5;

    SFX.play(hit, desc);
}
```

### Siguiendo una entidad (fuente de sonido en movimiento)

Si un sonido debe moverse con una entidad (zumbido del motor, proyectil zumbador, etc.), establece `entity_to_follow`:

```go
start_engine_loop :: proc(entity: Entity) -> u64 {
    engine := get_asset(SFX_Asset, "sfx/engine_loop.wav");

    desc := SFX.default_sfx_desc();
    desc.entity_to_follow = entity.id;
    desc.loop = true;
    desc.channel = .MUSIC; // opcional: tratarlo como música/ambientación en lugar de SFX

    // Devuelve un ID que puede usarse para actualizar o detener el sonido.
    return SFX.play(engine, desc);
}
```

{% hint style="info" %}
Si `entity_to_follow` está establecido, normalmente no necesitas llamar también a `set_position` (aunque no pasa nada si lo haces).
{% endhint %}

### Sonidos dirigidos a un jugador

Establece `specific_to_player` cuando el servidor deba rastrear el sonido pero solo un cliente deba oírlo realmente:

```go
play_private_ping :: proc(player: Player) {
    click := get_asset(SFX_Asset, "sfx/click.wav");

    desc := SFX.default_sfx_desc();
    desc.specific_to_player = player;
    desc.volume = 0.7;

    SFX.play(click, desc);
}
```

### Reproducción en bucle + detención

`SFX.play` devuelve un ID que puedes detener más tarde:

```go
sound_id := 0.(u64);

start_loop :: proc() {
    loop := get_asset(SFX_Asset, "sfx/ambience.wav");

    desc := SFX.default_sfx_desc();
    desc.loop = true;
    desc.volume = 0.6;
    desc.loop_timeout = 60; // seguridad: se detiene automáticamente después de ~60 s si lo olvidas

    sound_id = SFX.play(loop, desc);
}

stop_loop :: proc() {
    if sound_id != 0 {
        SFX.stop(sound_id);
        sound_id = 0;
    }
}
```

### Sonidos repetidos rápidamente y variación

Para sonidos repetitivos rápidos como pasos o armas automáticas, asigna a cada evento lógico un `ID único`. Asocia un índice de evento monótono sincronizado a su entidad origen cuando el sonido no siga a esa entidad:

```go
footstep_id += 1; // un campo u64 sincronizado en el estado del juego

desc := SFX.default_sfx_desc();
desc.set_position(entity.world_position);
desc.unique_id = mix_u64(entity.id, footstep_id);
SFX.play(get_asset(SFX_Asset, "$AO/sfx/Footsteps/footstep_01.wav"), desc);
```

El mismo evento lógico debe conservar el mismo `ID único` en el cliente, el servidor y cada resimulación. Cada evento cercano distinto debe obtener un valor diferente. Derívalo solo de un índice de evento que esté sincronizado y sea estable durante la predicción. `entity_to_follow` participa en la identidad del sonido, así que cuando se asigne, usa el índice del evento directamente en lugar de mezclar el ID de la entidad en `ID único`.

Una variación sutil puede evitar que los sonidos reproducidos con frecuencia se vuelvan monótonos:

```go
desc := SFX.default_sfx_desc();
desc.volume_perturb = 0.2;
desc.speed_perturb  = 0.15;
SFX.play(get_asset(SFX_Asset, "sfx/footstep.wav"), desc);
```

{% hint style="info" %}
Mantén `volume_perturb` / `speed_perturb` sutiles. Los valores por encima de \~`0.3` normalmente suenan mal.
{% endhint %}

### Predicción y asignación de objetivo

Llama a los sonidos de gameplay desde la misma ruta predicha compartida que el evento que los causó. El sistema de sonido reconcilia el sonido predicho de un cliente con el resultado del servidor, así que no envuelvas `SFX.play` en `Game.is_server()` o `player.is_local()`.

```go
play_round_victory :: proc(position: v2) {
    desc := SFX.default_sfx_desc();
    desc.set_position(position);
    SFX.play(get_asset(SFX_Asset, "sfx/round_victory.wav"), desc);
}
```

Para un sonido que solo un jugador debe oír, establece `desc.specific_to_player`. Esto mantiene el evento en el código de gameplay compartido mientras dirige la reproducción al cliente de ese jugador.

## Hápticos

`Haptics.play_impact` activa la respuesta háptica de impacto en el dispositivo local. Llámalo solo para el jugador local:

```go
My_Player :: class : Player_Base {
    play_local_impact :: method() {
        if !is_local() return;

        Haptics.play_impact(.MEDIUM);
    }
}
```

Elige `.LIGHT`, `.MEDIUM`, o `.HEAVY`. Los hápticos actualmente funcionan en iOS. Las llamadas en Android, la web, Windows y los servidores no hacen nada.

Los hápticos no se sincronizan, no se dirigen ni se deduplican. Actívalos en el punto de interacción local en lugar de desde código de gameplay de toda la escena.
