Home | Clases | Unity: Plataformas en 3D | Receta 6: Sonidos

Receta 6: Sonidos


Introducción

Un elemento crucial en cualquier videojuego es el sonido.

Te invito a conocer mi Curso de Sonido para Videojuegos en el que explico cómo funciona el sonido digital, cuales son los formatos utilizados o cómo se diseña el sonido de un videojuego en líneas generales.

También te invito a realizar el Tutorial Oficial de Audio en Unity (en inglés).

No obstante, en esta receta lo que vamos a conseguir es lo siguiente:

  • Reproducir una música de fondo durante todo el juego (musica.mp3)
  • Reproducir un sonido cuando se recoja una moneda (moneda.mp3)
  • Reproducir un sonido cuando salta el jugador (salto.mp3)
  • Reproducir un sonido desde que se recoge una cápsula hasta que termina la potencia de salto extra (saltoExtra.mp3)
  • Reproducir un sonido cuando se pierda una vida (perderVida.mp3)
  • Reproducir un sonido cuando se gane una vida (ganarVida.mp3)
  • Reproducir un sonido cuando se termine el juego y gane (finGanar.mp3)
  • Reproducir un sonido cuando se termine el juego y pierda (finPerder.mp3)
  • Reproducir un sonido para la plataforma rotatoria que ira subiendo de intensidad a medida que nos acerquemos a ella (rotatoria.mp3)
  • Poder gestionar todos los niveles de esos sonidos mediante un sistema de mezcla

Todos estos archivos de audio ya preparados los puedes obtener en ESTE ENLACE o usar los tuyos propios, y puedes comprobar el objetivo final haciendo clic AQUÍ.


El Audio en Unity

Una vez descargados los archivos, arrástralos a una nueva carpeta dentro de Assets llamada Audio.

Según el Manual de Unity:

  • Las características de Audio de Unity incluyen un sonido completo 3D spatial (espacial), una mezcla y mastering en tiempo real, jerarquías de mezcladores, snapshots y efectos pre-definidos.
  • Unity puede importar archivos audio en formatos AIFF, WAV, MP3 y Ogg de la misma manera que otros assets, simplemente arrastrando los archivos al panel del Proyecto.
  • Unity puede acceder a los micrófonos del computador desde un script y crear Audio Clips mediante una grabación directa mediante la clase Microphone.

Para empezar a trabajar con el audio, debemos diferenciar 4 elementos principales (si bien hay otros que conoceremos más adelante):

Audio Clip
Los Audio Clips contienen información de audio utilizada por Audio Sources. Unity soporta mono, stereo y assets de audio multi-canal (hasta ocho canales).

Audio Source
El Audio Source reproduce un Audio Clip en la escena. El clip se puede reproducir en un Audio Listener o a través de un Audio Mixer.

Audio Listener
El Audio Listener actúa como un dispositivo parecido a un micrófono. Recibe la entrada de cualquier Audio Source de la escena y reproduce el sonido a través de los alta-voces del computador. Para la mayoría de aplicaciones tiene mucho sentido adjuntar el listener a la Cámara principal, ya que cada escena solo puede contener un Audio Listener.

Audio Mixer
El Audio Mixer nos permite organizar las fuentes de audio en canales con control de volumen, así como aplicar diferentes efectos.


El mezclador de audio

Una vez entendido lo anterior y partiendo de la base de la receta 5, realiza los siguientes cambios:

  • Dentro de la carpeta Assets > Audio, crea un nuevo Audio Mixer con el nombre Mezclador.
  • Haz doble clic en él y, en el apartado Groups, añade dos grupos que dependan de Master llamados SFX (para los efectos sonoros) y Música (para la música de fondo)

De este modo, hemos configurado un mezclador para poder organizar mejor los niveles de nuestros audios. El resultado debería parecerse a este:




El Audio Source de la cámara

  • Añade un componente Audio Source a la Main Camera
  • Arrastra dentro de la propiedad Audio Clip el audio musica.mp3 (que deberías tener en Assets > Audio).
  • Haz clic en el círculo a la derecha de la propiedad Audio Mixer y selecciona el grupo Música en la ventana flotante.
  • Activa la propiedad Loop (reproducir en bucle)


El Audio Source de la plataforma Rotatoria

  • Añade un componente Audio Source a la plataforma Rotatoria
  • Arrastra dentro de la propiedad Audio Clip el audio rotatoria.mp3 (que deberías tener en Assets > Audio).
  • Haz clic en el círculo a la derecha de la propiedad Audio Mixer y selecciona el grupo SFX en la ventana flotante.
  • Activa propiedad Loop (reproducir en bucle)
  • Spatial Blend: 1 (3D)
  • Volume Rolloff: Linear Rolloff
  • Min Distance: 30
  • Max Distance: 39


NOTA: Las distancias son relativas al Listener, que en nuestro caso está en la Main Camera. También es posible ajustarlo moviendo los puntos de la curva.

En este momento, si ejecutas el juego deberías escuchar la música de fondo y el sonido de la Rotatoria, que son los únicos que se reproducen al arrancar el juego. Haz doble clic en el Mezclador y ajusta los niveles de los dos grupos (SFX y Música) para que suene a tu gusto y sin saturar.




Los Audio Sources del jugador

  • Añade 7 componentes Audio Source al Jugador.
  • Arrastra dentro de la propiedad Audio Clip de cada uno de ellos y en este orden los audios salto.mp3, moneda.mp3, saltoExtra.mp3, perderVida.mp3, ganarVida.mp3, finGanar.mp3 y finPerder.mp3 (que deberías tener en Assets > Audio).
  • Haz clic en el círculo a la derecha de la propiedad Audio Mixer de cada uno de ellos y selecciona el grupo SFX en la ventana flotante.
  • Desactiva la propiedad Play on Awake (reproducir al arrancar) de cada uno de ellos.
  • Modifica y comprende el siguiente código de JugadorController



using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class JugadorController : MonoBehaviour {

//Declarlo la variable de tipo RigidBody que luego asociaremos a nuestro Jugador
private Rigidbody rb;

//Declaro la variable pública velocidad para poder modificarla desde la Inspector window
[Range(1,10)]
public float velocidad = 5;

//Declaro la variable pública salto para poder modificarla desde la Inspector window
[Range(1,10)]
public float salto = 7;

//Límites en X y Z del Suelo
int limiteSuperior = 15;
int limiteInferior = -15;

//Variable para el número de vidas
int vidas = 3;

//Variable para asociar el objeto Texto Vidas
public Text textoVidas;

//variable para la posición inicial del jugador
Vector3 posicionInicial;

//Variable para asociar el objeto Mensajes
public Text textoMensajes;

//Variable para comprobar si debo seguir incrementando el tiempo transcurrido
public bool isTiempo = true;

//Variable para contabilizar las monedas
int monedas = 20;

//Variable para asociar el objeto Texto Coleccionables (que mostrará las monedas restantes)
public Text textoColeccionables;

//Variables para poder obtener el tiempo transcurrido del script Tiempo
public GameObject canvas;
Tiempo scriptTiempo;

//Variables para contar y activar el tiempo transcurrido de salto extra e incrementar la potencia
float tiempoSalto = 0;
bool isSaltoExtra = false;
int saltoExtra = 3;

//Variable para el sistema de partículas
public ParticleSystem particulas;

//Booleano para no asignar más de una vida
bool isVida = false;

//Array o lista de Audio Sources
AudioSource[] audioSources;
AudioSource audioSalto, audioMoneda, audioSaltoExtra, audioPerderVida, audioGanarVida, audioFinGanar, audioFinPerder;

void Start () {

//Capturo el rigidbody del jugador al iniciar el juego
rb = GetComponent<Rigidbody>();

//Inicializo el texto del contador de vidas
textoVidas.text = "Vidas: " + vidas;

//Inicio el texto de mensajes a vacío
textoMensajes.text = "";

//Capturo la posición inicial del jugador para cuando pierda una vida
posicionInicial = transform.position;

//Inicializo el texto del contador de monedas
textoColeccionables.text = "Monedas: " + monedas;

//Capturo el componente Tiempo del canvas
scriptTiempo = canvas.GetComponent<Tiempo>();

//Paro el sistema de partículas del jugador
particulas.Stop();

//Capturo e inicializo todos los audioSources (es un array de arriba a abajo)
audioSources = GetComponents<AudioSource>();
audioSalto = audioSources[0];
audioMoneda = audioSources[1];
audioSaltoExtra = audioSources[2];
audioPerderVida = audioSources[3];
audioGanarVida = audioSources[4];
audioFinGanar = audioSources[5];
audioFinPerder = audioSources[6];

}

void FixedUpdate () {

if (vidas > 0 && monedas > 0){
//Muevo el jugador (si tiene vidas y monedas pendientes)
moverJugador();
//Compruebo si se ha salido del suelo en X o Z
if (transform.position.x > limiteSuperior ||
transform.position.x < limiteInferior || 
transform.position.z > limiteSuperior ||
transform.position.z < limiteInferior){

//Quito una vida
quitarVida();
}
}
else if (vidas == 0 && isTiempo){ //añado isTiempo para que no repita los audios ni el fade in out
//Reproduzco el audio de finPerder
audioFinPerder.Play();
//Muestro mensaje (si no tiene vidas)
textoMensajes.text = "Juego Terminado";
if (isTiempo)
{
StartCoroutine(FadeInOutText(textoMensajes, 0.5f, 5.0f, false));
}
//Pongo isTiempo a false para que deje de contar el script Tiempo
isTiempo = false;
}
else if (monedas == 0 && isTiempo){ //añado isTiempo para que no repita los audios ni el fade in out
//Reproduzco el audio de finGanar
audioFinGanar.Play();
//Muestro mensaje (si ha completado el juego)
textoMensajes.text = "¡Has completado el juego en " + scriptTiempo.formatearTiempo();
textoMensajes.color = new Color(0, 255, 0); //Color verde
if (isTiempo)
{
StartCoroutine(FadeInOutText(textoMensajes, 0.5f, 5.0f, false));
}
//Pongo isTiempo a false para que deje de contar el script Tiempo
isTiempo = false;
}

//Compruebo si tiene potencia de salto extra
if (isSaltoExtra){
//Incremento tiempo
tiempoSalto += Time.deltaTime;
//Compruebo si llega a 10 segundos
if (tiempoSalto >= 10){
salto -= saltoExtra; //Asigno la potencia de salto original
tiempoSalto = 0; //Reseteo el contador de tiempo de salto extra
isSaltoExtra = false; //Asigno el boolenao de salto extra a false
particulas.Stop(); //Paro el sistema de partículas del jugador
}
}

//Compruebo si tengo que añadirle una vida
if (!isVida && monedas <= 10 && scriptTiempo.tiempo <= 30){
//Reproduzco el audio de perderVida
audioGanarVida.Play();
vidas++; //Añado una vida
//Muestro mensaje
textoMensajes.text = "Has ganado una vida";
if (!isVida)
{
StartCoroutine(FadeInOutText(textoMensajes, 0.5f, 0.3f, true));
}
//Pongo isTiempo a false para que deje de contar el script Tiempo
isVida = true; //Para añadir solo una vida

}

}

//Compruebo si el jugador está en el suelo antes de saltar (para que no vuele)
private bool isSuelo(){

//Genero el array de colisiones de la esfera/jugador pasando su centro y su radio
Collider[] colisiones = Physics.OverlapSphere(transform.position, 0.5f);
//Recorro ese array y si está colisionando con el suelo devuelvo true
foreach (Collider colision in colisiones){
if (colision.tag == "Suelo"){
return true;
}
}
return false;

}

//Quito una vida y muevo al jugador a la posición inicial
void quitarVida(){

//Reproduzco el audio de perderVida
audioPerderVida.Play();
//Resto una vida
vidas--;
//Actualizo el contador de vidas
textoVidas.text = "Vidas: " + vidas;
//Devuelvo el Jugador a su posición inicial y le quito la fuerza
transform.position = posicionInicial;
rb.velocity = Vector3.zero;

}

//Lógica necesaria para mover al jugador
void moverJugador(){

//Capturo el movimiento en horizontal y vertical de nuestro teclado
float movimientoH = Input.GetAxis("Horizontal");
float movimientoV = Input.GetAxis("Vertical");

//Genero el vector de movimiento asociado, teniendo en cuenta la velocidad
Vector3 movimiento = new Vector3(movimientoH * velocidad, 0.0f, movimientoV * velocidad);

//Aplico ese movimiento al RigidBody del jugador
rb.AddForce(movimiento);

//Si pulsa el botón de saltar y está en el suelo
if (Input.GetButtonDown("Jump") && isSuelo()){
//Reproduzco el audio de saltar
audioSalto.Play();
//Aplico el movimiento vertical con la potencia de salto
rb.velocity += Vector3.up * salto;
}

}

//Mostrar (y ocultar) texto con fade in/out
private IEnumerator FadeInOutText(Text texto, float duracionIn, float duracionOut, bool ocultar){

//Fade in
texto.color = new Color(texto.color.r, texto.color.g, texto.color.b, 0); //opacidad a 0
while (texto.color.a < 1.0f){
//Voy incrementando la opacidad hasta llegar a 1
texto.color = new Color(texto.color.r, texto.color.g, texto.color.b, texto.color.a + duracionIn * Time.deltaTime);
yield return null;
}

//Fade out
if (ocultar){
texto.color = new Color(texto.color.r, texto.color.g, texto.color.b, 1); //opacidad a 1
while (texto.color.a > 0.0f){
//Voy decrementando la opacidad hasta llegar a 0
texto.color = new Color(texto.color.r, texto.color.g, texto.color.b, texto.color.a - duracionOut * Time.deltaTime);
yield return null;
}
}

}

//Se ejecuta al entrar a un objeto con la opción isTrigger seleccionada
void OnTriggerEnter(Collider other) {

if (other.gameObject.CompareTag ("Moneda")){

//Reproduzco el audio de la moneda
audioMoneda.Play();
//Desactivo el objeto
other.gameObject.SetActive (false);
//Decremento el contador de monedas en uno (también se puede hacer como monedas = monedas -1)
monedas--;
//Actualizo el texto del contador de monedas
textoColeccionables.text = "Monedas: " + monedas;

}

else if (other.gameObject.CompareTag ("Capsula")){

//Reproduzco el audio de saltoExtra
audioSaltoExtra.Play();
//Desactivo el objeto
other.gameObject.SetActive (false);
//Incremento potencia de salto
salto += saltoExtra;
//Activo el booleano isSaltoExtra
isSaltoExtra = true;
//Inicio el sistema de partículas del jugador
particulas.Play();

}

}

}


Por supuesto, si lo consideras necesario, haz doble clic en el Mezclador y ajusta los niveles de los dos grupos (SFX y Música) para que suene a tu gusto y sin saturar. También puedes modificar los niveles de cada Audio Source por separado en su componente correspondiente.

Con esto estaría terminada la receta, espero que te haya resultado útil.

Puedes comprobar su funcionamiento haciendo clic AQUÍ.
Fecha de publicación: 19/09/2020
Asignaturas: desarrollo de entornos interactivos multidispositivo
Temas: unity
Utilizamos cookies propias y de terceros para mejorar su experiencia en la navegación. Al seguir navegando entendemos que acepta su uso.
Si lo desea, consulte nuestras políticas de privacidad y cookies
ENTENDIDO
[X] Cerrar

Contacta conmigo


[X] Cerrar

Acceso alumnos