8. EFECTOS.

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

Esta lección va a consistir en mejorar el aspecto visual del juego. Hasta ahora, hemos conseguido hacer funcionar nuestro batiscafo con unas físicas más o menos realistas, hemos puesto una serie de bichos que nos harán la vida imposible, y podemos recoger los tesoros y cargarlos en nuestro barco. Con esto, el juego es perfectamente jugable y sobra para echarse unos piques con los amigos. Pero no queremos sólo que funcione, sino que nos interesa que sea bonito y espectacular.

Esto lo vamos a conseguir añadiendo burbujas de aire que salgan de los motores de nuestro submarino y que vayan subiendo hacia la superficie. Sencillo, pero que le dará un toque bastante profesional.

Como tenemos que poner en práctica todo lo que hemos visto, vamos a hacer algo similar a nuestro anterior código. Lo importante es que lo conseguiremos con un paso tan sencillo como proponérselo.

Código:
///////////////////////////////////
/*  Direcciones                  */
///////////////////////////////////
#define DIR_DOWN    0
#define DIR_LEFT    1
#define DIR_RIGHT   2
Lo primero que hemos hecho al principio del código es definir 3 constantes. Ahora es pronto para comprenderlas, pero nos servirán para decidir hacia qué dirección se moverán las burbujas que se crean. Si la nave está subiendo, quiere decir que los motores inferiores estarán expulsando burbujas en dirección opuesta: o sea, hacia abajo. Y lo mismo con el resto de direcciones.

Código:
///////////////////////////////////
/*  Estructuras                  */
///////////////////////////////////
struct bubble_base
{
  int x;
  int y;
  float ah;
  float av;
};
A continuación creamos una estructura para las burbujas. Ésta contiene la posición y las aceleraciones horizontal y vertical. Esta misma estructura nos habría venido de perlas para la nave... si es que todavía no la habéis hecho. Las burbujas, al contrario que el batiscafo, se dirigirán hacia arriba en estado de reposo pero las fórmulas son las mismas.

Código:
std::vector<bubble_base> bubble_list;
Creamos una lista de burbujas, exactamente igual que hicimos con los bichos. Se llamará bubble_list. Cada elemento de la lista tendrá una burbuja en una posición, con unos valores de aceleración distintos.

Código:
void new_bubble(int x, int y, int dir)
{
  bubble_base b;
  b.x=x-4+rand()%9;
  b.y=y-4+rand()%9;
Lo siguiente es una función que nos creará una burbuja cerca de la posición x,y, y que se mueva hacia la dirección dir, como ya vimos arriba. Para eso, creamos una estructura llamada b donde guardaremos esos valores.

Código:
  switch(dir)
  {
    case DIR_DOWN:
      b.av=rand()%5;
      b.ah=rand()%2;
      break;
    case DIR_LEFT:
      b.av=rand()%2;
      b.ah=-rand()%5;
      break;
    case DIR_RIGHT:
      b.av=rand()%2;
      b.ah=rand()%5;
      break;
  }
Dependiendo de la dirección, le daremos unos valores distintos de aceleración. Por ejemplo, si la burbuja arranca moviéndose hacia abajo le daremos una aceleración vertical aleatoria hasta un valor de 5, y hacia los lados hasta un máximo de 2. Con esto conseguimos que las burbujas no se muevan en línea recta, sino que lo hagan con cierta inclinación y fuerza, como lo harían en el mundo real. Si las burbujas arrancan moviéndose hacia la izquierda o derecha, les daremos mayor aceleración en esa dirección.

Código:
  bubble_list.push_back(b);
}
Y finalmente, metemos la estructura b en la lista bubble_list, que es la que se dibujará en pantalla.

Código:
void reset()
{
...
  bubble_list.clear();
}
En la función reset añadimos una línea que limpie la lista de burbujas, para que cada partida empieze sin burbujas en pantalla... por lo menos hasta que se mueva la nave.

Código:
void read_game_keys()
{
...
  if(SDL_JoystickGetButton(joystick, GP2X_BUTTON_B))
  {
    if(ship_av>-8)
      ship_av-=0.3;
    if(rand()%3==0)
      new_bubble(ship_x+12,ship_y+22,DIR_DOWN);
  }
  else
  {
    if(ship_av<8)
      ship_av+=0.3;
  }
Las burbujas se crean con el movimiento de la nave, así que las añadiremos en la función read_game_keys(). Vamos hasta la línea que comprueba si pulsamos B (que hace que la nave suba hasta la superficie) y añadimos una línea nueva. Sólo crearemos una burbuja si al crear un número aleatorio (entre 0 y 2) el resultado es 0. En ese caso, llamaremos a la función que crea una burbuja new_bubble(). La posición de la burbuja será la parte central inferior de la nave: ship_x+12 y ship_y+22, y con la dirección hacia abajo: DIR_DOWN.

Código:
  if(SDL_JoystickGetButton(joystick, GP2X_BUTTON_LEFT) || SDL_JoystickGetButton(joystick, GP2X_BUTTON_UPLEFT) || SDL_JoystickGetButton(joystick, GP2X_BUTTON_DOWNLEFT))
  {
    if(ship_ah>-8)
      ship_ah-=0.3;
    if(rand()%3==0)
      new_bubble(ship_x+22,ship_y+7,DIR_RIGHT);
  }
Haremos lo mismo al pulsar IZQUIERDA. Crearemos una burbuja en la parte derecha de la nave que vaya hacia la derecha.

Código:
  if(SDL_JoystickGetButton(joystick, GP2X_BUTTON_RIGHT) || SDL_JoystickGetButton(joystick, GP2X_BUTTON_UPRIGHT) || SDL_JoystickGetButton(joystick, GP2X_BUTTON_DOWNRIGHT))
  {
    if(ship_ah<8)
      ship_ah+=0.3;
    if(rand()%3==0)
      new_bubble(ship_x+2,ship_y+7,DIR_LEFT);
  }
Y repetimos al pulsar DERECHA. Crearemos una burbuja en la parte izquierda de la nave que vaya hacia la izquierda.

Código:
void draw_game()
{
...
  // dibujamos las burbujas
  SDL_Rect rbubble;
  for(int i=0; i<bubble_list.size(); i++)
  {
    rbubble.x=bubble_list[i].x;
    rbubble.y=bubble_list[i].y;
    if(bubble)
      SDL_BlitSurface(bubble,NULL,screen,&rbubble);
  }
¿Cómo se dibujan las burbujas? Pues exactamente igual que los bichos. Creamos un bucle que recorra la lista bubble_list. Creamos una variable SDL_Rect llamada rbubble que almacene los datos de las burbujas. Y dibujamos con SDL_BlitSurface el gráfico de la burbuja en la pantalla. Al ser un gráfico pequeño, podemos pintar muchos.

Código:
void update_game()
{
...
  // mueve las burbujas
  for(int i=0; i<bubble_list.size(); i++)
  {
    bubble_list[i].x+=(int)bubble_list[i].ah-1+rand()%3;
    bubble_list[i].y+=(int)bubble_list[i].av;
De nuevo, hay que recordar que los movimientos y la IA en los juegos hay que hacerla en la función update_game() (al menos, de momento). Si se metieran estas comprobaciones y los movimientos al pulsar los botones de movimiento, por ejemplo, sería complicado hacer cosas como las pausas (que simplemente no ejecuta esta función).

Así que, recorremos en bucle la lista de burbujas bubble_list. A la posición x de cada burbuja le sumamos la aceleración horizontal más un número aleatorio de 0 a 2, para que todas las burbujas tengan unos pixels de movimiento impredecible lateralmente. Y a la posición vertical le sumamos la aceleración vertical.

Código:
    if(bubble_list[i].ah<0)
      bubble_list[i].ah+=0.3;
    if(bubble_list[i].ah>0)
      bubble_list[i].ah-=0.3;
    if(bubble_list[i].av>-1)
      bubble_list[i].av-=0.3;
Las burbujas se moverán lateralmente, pero al final se detendrán y seguirán subiendo hasta la superficie. Así que comprobamos la aceleración horizontal, si es mayor que 0 la decrementamos y si es menor la incrementamos... al final, su valor será 0. Si la aceleración vertical es mayor que -1, la decrementamos. Así nos aseguramos que al final su valor será -1, y por tanto las burbujas subirán siempre, ya que a su posición y se le restará 1 en cada comprobación.

Código:
    if(bubble_list[i].y<48)
    {
      bubble_list.erase(bubble_list.begin()+i);
      i--;
    }
  }
Finalmente, si la burbuja llega a la superficie: o sea, su posición y es menor que 48, desaparece. ¡Las burbujas no flotan por el aire!

Y con esto, ya tenemos todo. Cada vez que pulsemos una dirección, nuestro batiscafo irá creando burbujas a su paso. No afectarán ni a los bichos ni a la nave, pero quedará tremendamente espectacular. Y nos ha costado... ¡unas pocas líneas!

Podéis jugar fácilmente con los valores de creación de las burbujas: dándole más velocidad, menos velocidad, que aparezcan más veces, que aparezcan más burbujas, que se mantengan un rato en la superficie... bueno, se pueden hacer montones de cosas y podéis ponerlas en práctica en el código.

Recordad que hacer un juego es muy fácil, y lo estamos haciendo siguiendo unos pasos muy sencillos. Si cada vez que programáis os centráis en una parte específica del proceso, al final, el juego aparece solo. ¡Probadlo!