9. SONIDOS.

Tenéis el proyecto en: Tutorial de programación c++ (9/11)

Ya queda muy poco para acabar el proyecto. Una de las cosas que más vida le da a los juegos son los sonidos. Con unos ligeros toques para ambientar la aventura, se puede conseguir una inmersión total, que en este caso nos va a venir de perlas.

Pero empecemos desde el principio.

Código:
///////////////////////////////////
/*  Librerías a utilizar         */
///////////////////////////////////
#include <SDL/SDL_mixer.h>
Lo primero es añadir la librería de sonido de la SDL. Para eso, añadimos este include en la cabecera del código.

Código:
///////////////////////////////////
/*  Variables globales           */
///////////////////////////////////
int volume=120;
En las variables globales añadimos la variable volume para controlar el volumen (...claro). La dejamos en el valor 120.

Código:
//sonidos
Mix_Chunk *sound_bubble;
Mix_Chunk *sound_gold;
Mix_Chunk *sound_hit;
Mix_Chunk *sound_roar;
Mix_Chunk *sound_water;
Añadimos 5 variables para almacenar los sonidos que vamos a usar: burbujas, ruido metálico al dejar el oro en el barco, golpe al recoger el oro, un rugido cuando los bichos nos comen y sonido de agua cíclico (para que ambiente el escenario submarino).

Código:
void init_game()
{
...
  Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, AUDIO_S16, MIX_DEFAULT_CHANNELS, 256);
En la función init_game() arrancamos el motor de audio con la función Mix_OpenAudio().

Código:
  sound_bubble=Mix_LoadWAV("data/bubble.wav");
  sound_gold=Mix_LoadWAV("data/gold.wav");
  sound_hit=Mix_LoadWAV("data/hit.wav");
  sound_roar=Mix_LoadWAV("data/roar.wav");
  sound_water=Mix_LoadWAV("data/water.wav");
Lo siguiente es cargar en memoria los sonidos con Mix_LoadWAV(). Los sonidos están guardados en la carpeta data del proyecto.

Código:
  Mix_PlayChannel(-1,sound_water,-1);
Y reproducimos el sonido del agua de forma cíclica, para que se escuche durante todo el juego. El primer parámetro -1, indica que lo puede reproducir por cualquier canal disponible, el segundo parámetro es el sonido a reproducir, y el tercero -1, indica que es cíclico.

Código:
void end_game()
{
...
  Mix_FreeChunk(sound_bubble);
  Mix_FreeChunk(sound_gold);
  Mix_FreeChunk(sound_hit);
  Mix_FreeChunk(sound_roar);
  Mix_FreeChunk(sound_water);
  Mix_CloseAudio();
En la función end_game() liberamos la memoria ocupada por los sonidos y cerramos el motor de audio.

Código:
void new_bubble(int x, int y, int dir)
{
...
  Mix_PlayChannel(-1,sound_bubble,0);
Al final de la función new_bubble(), que se ejecuta cada vez que se crea una burbuja, añadimos una línea para que se reproduzca el sonido de burbuja. Cada vez que creemos una burbuja, lo haremos con un sonido particular.

Código:
void finish()
{
...
  Mix_PlayChannel(-1,sound_roar,0);
En la función finish(), que se ejecuta cuando morimos, añadimos una línea para reproducir el sonido del rugido... ¡un castigo a nuestra torpeza!

Código:
void read_menu_keys()
{
...
          case GP2X_BUTTON_VOLUP:
            if(volume<120)
            {
              volume+=10;
              Mix_Volume(-1,volume);
            }
            break;
          case GP2X_BUTTON_VOLDOWN:
            if(volume>0)
            {
              volume-=10;
              Mix_Volume(-1,volume);
            }
            break;
En la función read_menu_keys() añadimos una comprobación para ver si pulsamos las teclas de volumen. Podremos pulsar subir volumen hasta un máximo de 120 y hasta un mínimo 0. Para establecer el volumen del juego, ejecutamos Mix_Volume() con el valor actual de volumen.

Código:
void read_game_keys()
{
...
       case GP2X_BUTTON_VOLUP:
            if(volume<120)
            {
              volume+=10;
              Mix_Volume(-1,volume);
            }
            break;
          case GP2X_BUTTON_VOLDOWN:
            if(volume>0)
            {
              volume-=10;
              Mix_Volume(-1,volume);
            }
            break;
En read_game_keys() hacemos lo mismo para comprobar el volumen. Esto se podría haber hecho de otra forma para no repetir código, pero ya veremos en otros tutoriales como juntar el código de forma óptima. Así que, de momento, copiar y pegar...

Código:
void update_game()
{
...
  // comprueba si coge un tesoro y actualiza el que lleva encima
  for(int i=0; i<4; i++)
  {
    if(gold_list[i].exist)
    {
      if(gold_list[i].carried)
      {
        gold_list[i].x=ship_x;
        gold_list[i].y=ship_y+24;
      }
      if(!ship_load && ship_y>=204 && ship_x>gold_list[i].x-4 && ship_x<gold_list[i].x+4)
      {
        gold_list[i].carried=1;
        ship_load=1;
        Mix_PlayChannel(-1,sound_hit,0);
      }
    }
  }
En update_game() comprobamos si cogemos un tesoro. Añadiremos una línea para que, además de cargarlo encima, se emita un sonido con un golpe que indique que lo hemos capturado.

Código:
  // comprueba si deja el tesoro en el barco
  if(ship_load && ship_y<=48 && ship_x>=136 && ship_x<160)
  {
    for(int i=0; i<4; i++)
    {
      if(gold_list[i].carried)
      {
        gold_list[i].carried=0;
        gold_list[i].exist=0;
        ship_load=0;
        score++;
        Mix_PlayChannel(-1,sound_gold,0);
      }
    }
  }
Y si dejamos el tesoro en el barco hacemos lo mismo: un sonido de oro que indique que nos estamos forrando. Todo se hace con la función Mix_PlayChannel().

Código:
void read_pause_keys()
{
...
          case GP2X_BUTTON_VOLUP:
            if(volume<120)
            {
              volume+=10;
              Mix_Volume(-1,volume);
            }
            break;
          case GP2X_BUTTON_VOLDOWN:
            if(volume>0)
            {
              volume-=10;
              Mix_Volume(-1,volume);
            }
            break;
En read_pause_keys() volvemos a comprobar el volumen.

Y esto es todo. Con unas pocas líneas hemos conseguido añadir unos sonidos estridentes y llamativos que hacen de la experiencia del juego algo más divertido. ¡No os olvidéis de experimentar!