ArkaLlop - Clon del Arkanoid

Llop Site Home > JAVA > ArkaLlop > Bola

Bola:

Clase Bola

package arkallop;

import java.lang.*;
import java.awt.*;

/** Título: ArkaLlop
    Descripción: Prácticas capítulo 11, ejercicio 4
    Copyright: Copyright (c) 2005
    Empresa: Casa_Llop
    @author Albert_Lobo
    @version 1.0 */

/** Clase que representa las bolas de la partida.
    Contiene un array con todas las bolas, que no son más que 'sprites'.
    Trata todo lo respectivo a ellos: su movimiento, si chocan contra otro 'sprite' o las paredes,
    sus animaciones... */
public class Bola implements Runnable
{
  /** Variable estática que representa el tamaño normal de las bolas. */
  private final static byte TAMANO_NORMAL = 0;
  /** Variable estática que representa el tamaño doble de las bolas. */
  private final static byte TAMANO_DOBLE = 1;

  // Imágenes de una bola viva; una de cuando tiene el tamaño normal, y otra de cuando es grande.
  private Image imgViva;
  private Image imgVivaGrande;

  // Arrays para las imágenes de las animaciones (las de una bola petando -tamaño normal y
  // doble-, y la de una bola cambiando de tamaño),
  // y para sus correspondientes fotogramas.
  private Image imgMuerta [];
  private final static byte secuenciaMuere []
      = new byte [] {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
  private Image imgMuertaGrande [];
  private final static byte secuenciaMuereGrande []
      = new byte [] {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
  private Image imgCmbTamano [];
  private final static byte secuenciaCambiaTamano []
      = new byte [] {0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 1, 1};

  // El diámetro, el radio, y el tamaño ('TAMANO_NORMAL' o 'TAMANO_DOBLE') de las bolas.
  private byte diam;
  private byte rad;
  private byte tamano;
  // Variable que controla si las bolas están cambiando de tamaño o no.
  private boolean cambiaTamano;

  // Cada bola debe tener constancia de la pala, los aliens, los ladrillos y los bonos,
  // para saber cómo rebotar.
  private Pala pala;
  private Aliens aliens;
  private Nivel nivel;
  private Bonos bono;

  // El hilo que controlará las bolas.
  private Thread hilo;

  // El número de bolas en pantalla, el número de bolas vivas,
  // y finalmente una tabla para las bolas individuales.
  private byte numAlmsAnim;
  private byte numAlmsReal;
  private AlmaBola almas [];

  // La partida.
  private Partida partida;

  /** Constructor.
      @param nuevaPartida Partida  Esta partida. */
  public Bola (Partida nuevaPartida)
  {
    // Inicializamos las variables.
    partida = nuevaPartida;
    inicializaImagenes ();
    almas = new AlmaBola [] {new AlmaBola (), new AlmaBola (), new AlmaBola ()};
    hilo = new Thread (this);
    variablesPorDefecto ();
  }

  /** Ajusta las variables para el comienzo de una partida. */
  public void variablesPorDefecto ()
  {
    tamano = TAMANO_NORMAL;
    diam = (byte) imgViva.getWidth (null);
    rad = (byte) (diam / 2);
    numAlmsAnim = 1;
    numAlmsReal = 1;
    almas [0].setCoords (242, 494);
    almas [0].setVect ((Math.random () < .5) ? 4 : -4, -4);
    almas [0].vive = true;
  }

  /** Método que cambia el tamaño de las bolas. */
  public void cambiaTamano ()
  {
    // Si la bola tiene el tamaño normal, crece; si no, mengua.
    if (tamano == TAMANO_NORMAL)
      bolaCrece ();
    else
      bolaMengua ();
  }

  /** Hace menguar las bolas. */
  private void bolaMengua ()
  {
    // Ajusta las variables relativas al tamaño de las bolas.
    cambiaTamano = true;
    tamano = TAMANO_NORMAL;
    diam = (byte) imgViva.getWidth (null);
    rad = (byte) (diam / 2);
    // Tras medio segundo, las bolas dejan de crecer, y reajustamos las coordenadas de las bolas.
    new java.util.Timer ().schedule
        (new java.util.TimerTask () { public void run ()
             {
               cambiaTamano = false;
               for (byte i = 0; i < numAlmsReal; i++)
               {
                 almas [i].coordX += rad;
                 almas [i].coordY += rad;
               }
             } }, 500);
  }

  /** Hace crecer las bolas. */
  private void bolaCrece ()
  {
    // Ajustamos las coordenadas de cada bola, vigilando que no se incrusten en una pared.
    for (byte i = 0; i < numAlmsReal; i++)
    {
      if (almas [i].coordX - rad < 30)
        almas [i].coordX = 30;
      else if (almas [i].coordX + diam + rad > 470)
        almas [i].coordX = 438;
      else
        almas [i].coordX -= rad;
      if (almas [i].coordY - rad < 40)
        almas [i].coordY = 40;
      else
        almas [i].coordY -= rad;
    }
    // Ajusta las variables relativas al tamaño de las bolas.
    tamano = TAMANO_DOBLE;
    diam = (byte) imgVivaGrande.getWidth (null);
    rad = (byte) (diam / 2);
    cambiaTamano = true;
    // La bola dejará de crecer al cabo de medio segundo.
    new java.util.Timer ().schedule
        (new java.util.TimerTask () { public void run () { cambiaTamano = false; } }, 500);
  }

  /** Arranca el hilo que mueve las bolas. */
  public void arrancaHilo ()
  {
    if (!hilo.isAlive ())
      hilo.start ();
  }

  /** Mueve una bola horizontalmente por la pantalla.
      @param indice byte   Índice de la bola en cuestión.
      @param offset short  Distancia en píxels que se moverá la bola. */
  public void mueveHorizontalmente (byte indice, short offset)
  {
    float nuevaCoordXAlma = almas [indice].coordX += offset;
    // No ajustamos la coordenada X de la bola si se ha incrustado en una pared.
    if ((nuevaCoordXAlma >= 30) && (nuevaCoordXAlma + diam <= 470))
      almas [indice].coordX = nuevaCoordXAlma;
  }

  /** Activa -da 'vida' a- las tres bolas para que entren en juego.
      Si las tres bolas ya están en juego, o hay alguna muriendo, no pasa nada. */
  public void tripAlms ()
  {
    // Si hay una bola muriendo, no hacer nada.
    if (hayBolaMuriendo ())
      return;

    // Activa una o dos bolas según haya dos o una en juego. Se separan de la original.
    switch (numAlmsAnim)
    {
      case 1:
        almas [1].setCoords (almas [0].coordX, almas [0].coordY);
        almas [1].setVect (almas [0].vectX, -almas [0].vectY);
        almas [1].vive = true;
      case 2:
        almas [2].setCoords (almas [0].coordX, almas [0].coordY);
        almas [2].setVect (-almas [0].vectY, -almas [0].vectX);
        almas [2].vive = true;
    }
    // Ponemos el número de bolas en juego y en pantalla a tres.
    numAlmsAnim = 3;
    numAlmsReal = 3;
  }

  /** Permite saber si alguna bola está muriendo.
      @return boolean  'true' si alguna bola está muriendo, y 'false' si no. */
  private boolean hayBolaMuriendo ()
  {
    for (byte i = 0; i < numAlmsAnim; i++)
      if (!almas [i].vive)
        return true;
    return false;
  }

  /** Inicializa las imágenes/fotogramas para aminar las bolas. */
  public void inicializaImagenes ()
  {
    imgViva = ArkaLlop.getImagen ("bolaViva.gif");
    imgVivaGrande = ArkaLlop.getImagen ("bolaVivaGrande.gif");

    byte i;
    imgMuerta = new Image [5];
    for (i = 0; i < imgMuerta.length; i++)
      imgMuerta [i] = ArkaLlop.getImagen ("bolaMuerta" + i + ".gif");
    imgMuertaGrande = new Image [5];
    for (i = 0; i < imgMuertaGrande.length; i++)
      imgMuertaGrande [i] = ArkaLlop.getImagen ("bolaMuertaGrande" + i + ".gif");
    imgCmbTamano = new Image [3];
    for (i = 0; i < imgCmbTamano.length; i++)
      imgCmbTamano [i] = ArkaLlop.getImagen ("bolaCreciendo" + i + ".gif");
  }

  /** Define la pala.
      @param nuevaPala Pala  La pala. */
  public void setPala (Pala nuevaPala)
  {
    pala = nuevaPala;
  }

  /** Define los aliens.
      @param nuevosAliens Aliens  Los aliens. */
  public void setAliens (Aliens nuevosAliens)
  {
    aliens = nuevosAliens;
  }

  /** Define el nivel.
      @param nuevoNivel Nivel  El nivel */
  public void setNivel (Nivel nuevoNivel)
  {
    nivel = nuevoNivel;
  }

  /** Define los bonos.
      @param nuevoBono Bonos  Los bonos. */
  public void setBonos (Bonos nuevoBono)
  {
    bono = nuevoBono;
  }

  /** Dupilca la velocidad de las bolas. */
  public void acelera ()
  {
    for (byte i = 0; i < numAlmsAnim; i++)
      if (almas [i].vive)
      {
        almas [i].vectX *= 2;
        almas [i].vectY *= 2;
      }
  }

  /** Reduce la velocidad de las bolas a la mitad. */
  public void decelera ()
  {
    for (byte i = 0; i < numAlmsAnim; i++)
      if (almas [i].vive)
      {
        almas [i].vectX *= .5;
        almas [i].vectY *= .5;
      }
  }

  /** Permite conocer el diámetro de las bolas.
      @return byte  Diámetro de las bolas. */
  public byte getDiam ()
  {
    return diam;
  }

  /** Permite conocer el radio de las bolas.
      @return byte  El radio de las bolas. */
  public byte getRad ()
  {
    return rad;
  }

  /** Permite saber cuántas bolas hay que mostrar en pantalla.
      @return byte  Número de bolas a mostrar en pantalla. */
  public byte getNumAlmsAnim ()
  {
    return numAlmsAnim;
  }

  /** Método propio del interfaz 'Runnable'.
      Gestiona la interacción de los aliens con su entorno. */
  public synchronized void run ()
  {
    // Variable que almacenará los milisegundos entre vuelta y vuelta del bucle.
    long tmpOffset = System.currentTimeMillis ();
    while (true)
    {
      // Mientras el juego esté pausado, que duerma un poco el hilo.
      while (partida.getPausa ())
        try
        {
          hilo.sleep (10);
        } catch (InterruptedException e) {}

        // En este bucle gestionamos las bolas en pantalla.
        for (byte i = 0; i < numAlmsAnim; i++)
        {
          // La bola debe estar viva.
          if (almas [i].vive)
          {
            // La bola no se parará nunca, y aquí nos aseguramos de ello.
            if (!almas [i].pegadaPala)
            {
              float vectCuad = (float) (Math.pow (almas [i].vectX, 2) + Math.pow (almas [i].vectY, 2));
              if (vectCuad < 6)
              {
                float relacion = 6 / vectCuad;
                almas [i].vectX *= relacion;
                almas [i].vectY *= relacion;
              }
            }
            else
              continue;

            // Mueve la bola.
            almas [i].coordX += almas [i].vectX;
            almas [i].coordY += almas [i].vectY;

            // Comprueba si ha chocado contra la pala, la pared, un bono, un ladrillo, un alien,
            // otra pelota, o un tiro.
            if ((almas [i].coordY + diam > 510) && (almas [i].vectY > 0))
              choquePala (i);
            choquePared (i);
            if (!almas [i].vive)
              continue;
            choqueBono (i);
            choqueLadrillo (i);
            if (aliens.getNumAliensAnim () != 0)
              choqueAlien (i);
            if (numAlmsAnim > 1)
              choquePelota (i);
            if (pala.getNumTirosAnim () != 0)
              choqueTiro (i);
        }
      }

      // El vector de la pala, a cero.
      pala.setVectX ((short) 0);

      // Se calcula cuánto ha pasado desde que empezó la vuelta del bucle.
      // Este valor se descontará del tiempo de descanso del hilo.
      // El hilo descansaría 30 milisegundos, así que si 'tmpOffset' es demasiado,
      // lo ajustamos para que el hilo duerma al menos 5 milésimas.
      tmpOffset = System.currentTimeMillis () - tmpOffset;
      if (tmpOffset > 25)
        tmpOffset = 25;
      try
      {
        Thread.sleep (30 - tmpOffset);
      } catch (InterruptedException e) {}
      tmpOffset = System.currentTimeMillis ();
    }
  }

  /** Devuelve la coordenada X de una bola.
      @param indice byte  Índice de la bola en cuestión.
      @return float       Su coordenada X. */
  public float getCoordX (byte indice)
  {
    return almas [indice].coordX;
  }

  /** Devuelve la coordenada Y de una bola.
      @param indice byte  Índice de la bola en cuestión.
      @return float       Su coordenada Y. */
  public float getCoordY (byte indice)
  {
    return almas [indice].coordY;
  }

  /** Devuelve el componente X del vector de movimiento de una bola.
      @param indice byte  Índice de la bola en cuestión.
      @return float       Componente X de su vector de movimiento. */
  public float getVectX (byte indice)
  {
    return almas [indice].vectX;
  }

  /** Devuelve el componente Y del vector de movimiento de una bola.
      @param indice byte  Índice de la bola en cuestión.
      @return float       Componente Y de su vector de movimiento. */
  public float getVectY (byte indice)
  {
    return almas [indice].vectY;
  }

  /** Devuelve el fotograma correspondiente a una bola.
      @param indice byte  Índice de la bola en el array.
      @return Image       Imagen de la bola. */
  public Image getImagen (byte indice)
  {
    // A cada llamada a este método se devuelve la siguiente imagen de la secuencia;
    // cuando el índice llega al final, vuelve a cero.
    if (almas [indice].vive)
    {
      if (cambiaTamano)
      {
        if (almas [indice].indiceAnimCambiaTamano == 12)
          almas [indice].indiceAnimCambiaTamano = 0;
        return imgCmbTamano [secuenciaCambiaTamano [almas [indice].indiceAnimCambiaTamano++]];
      }
      else if (tamano == TAMANO_DOBLE)
        return imgVivaGrande;
      else
        return imgViva;
    }
    else
    {
      if (tamano == TAMANO_DOBLE)
      {
        if (almas [indice].indiceAnimMuereGrande == 15)
          almas [indice].indiceAnimMuereGrande = 0;
        return imgMuertaGrande [secuenciaMuereGrande [almas [indice].indiceAnimMuereGrande++]];
      }
      else
      {
        if (almas [indice].indiceAnimMuere == 15)
          almas [indice].indiceAnimMuere = 0;
        return imgMuerta [secuenciaMuere [almas [indice].indiceAnimMuere++]];
      }
    }
  }

  /** 'Mata' una bola.
      @param indice byte  Índice de la bola en cuestión. */
  private synchronized void mataBola (byte indice)
  {
    almas [indice].vive = false;         // La bola 'muere'.
    defragAlmaArr (indice);              // Intercambia su posición en el array con la última 'viva'.
    numAlmsReal--;                       // Una bola viava menos.
    partida.suena (Sonido.PIERDE_VIDA);  // Efecto sonoro.
    // Tras medio segundo, terminamos las gestiones necesarias para matar una bola.
    almas [numAlmsReal].alarma.schedule
        (new java.util.TimerTask () { public void run () { numAlmsAnim--; gestionaMuerte (); } }, 500);
  }

  /** Al morir una bola, pueden pasar una serie de cosas:
        - Si había más de una bola en juego, no pasa nada.
        - Si era la única, se pierde una vida.
        - Y si además ya no quedan 'vidas' en la partida, 'Game Over'. */
  private void gestionaMuerte ()
  {
    if (numAlmsAnim == 0)
    {
      if (partida.getVidas () == 0)
      {
        partida.suena (Sonido.GAME_OVER);
        partida.terminaPartida (Partida.DERROTA);
      }
      else
        partida.pierdeVida ();
    }
  }

  /** Método que intercambia la posición de dos bolas en la tabla:
      la indicada con el argumento con la última de los 'vivas' en la tabla.
      Se utilizará para mantener las bolas vivas en las primeras posiciones de la tabla,
      y las muertas al final.
      @param indice byte  Índice de la bola que se pondrá en el lugar de la última 'viva'. */
  private synchronized void defragAlmaArr (byte indice)
  {
    if (indice != numAlmsReal - 1)
    {
      AlmaBola aux = almas [numAlmsReal - 1];
      almas [numAlmsReal - 1] = almas [indice];
      almas [indice] = aux;
    }
  }

  /** Permite conocer si una bola está pegada a la pala.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola esté pegada a la pala o no. */
  public boolean pegadaPala (byte indice)
  {
    return almas [indice].pegadaPala;
  }

  /** Despega una bola de la pala.
      @param indice byte  Índice de la bola en cuestión. */
  public void despegaPala (byte indice)
  {
    // La bola despega para un lado u otro según en qué mitad de la pala se encuentre.
    if (almas [indice].coordX + rad < pala.getCoordX () + (pala.getAnchura () / 2))
      almas [indice].vectX = -4;
    else
      almas [indice].vectX = 4;
    almas [indice].vectY = -4;
    // Despega la bola, y suena un ruidito.
    almas [indice].pegadaPala = false;
    partida.suena (Sonido.BOLA_DESPEGA);
  }

  /** Mira si una bola ha chocado con la pala. De ser así, la bola rebota.
      La pala también lleva cuenta de su velocidad, por lo que se le puede dar efecto a la bola.
      @param indice byte  Índice del alien. */
  private void choquePala (byte indice)
  {
    // Comenzanos pensando que no hay choque.
    boolean hayChoque = false;

    // ¿Ha chocado con la superficie de la pala?
    if ((almas [indice].coordX + rad >= pala.getCoordX () + 6)
       && (almas [indice].coordX + rad <= pala.getCoordX () + pala.getAnchura () - 6))
    {
      almas [indice].coordY = (short) (2 * (510 - diam) - almas [indice].coordY);
      almas [indice].vectX += pala.getVectX ();
      almas [indice].vectY *= -1;
      hayChoque = true;
      partida.suena (Sonido.BOLA_IMPACTA);
    }
    // ¿Ha chocado con alguno de los bordes de la pala?
    else if ((almas [indice].coordX + rad > pala.getCoordX () + pala.getAnchura () - 6)
       && (almas [indice].coordX + (diam * .125) <= pala.getCoordX () + pala.getAnchura ()))
    {
      short vectChoqX = (short) ((almas [indice].coordX + rad) - (pala.getCoordX () + pala.getAnchura () - 6));
      short vectChoqY = (short) ((almas [indice].coordY + rad) - 520);
      float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
      if (modCuadVectChoq < Math.pow (rad + 8, 2))
      {
        ajstVectChoqBrd (-almas [indice].vectX, -almas [indice].vectY, vectChoqX, vectChoqY,
                         modCuadVectChoq, indice);
        almas [indice].vectX += pala.getVectX ();
        hayChoque = true;
        partida.suena (Sonido.BOLA_IMPACTA);
      }
    }
    else if ((almas [indice].coordX + (diam * .875) >= pala.getCoordX ())
       && (almas [indice].coordX + rad < pala.getCoordX () + 6))
    {
      short vectChoqX = (short) ((almas [indice].coordX + rad) - (pala.getCoordX () + 6));
      short vectChoqY = (short) ((almas [indice].coordY + rad) - 520);
      float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
      if (modCuadVectChoq < Math.pow (rad + 8, 2))
      {
        ajstVectChoqBrd (-almas [indice].vectX, -almas [indice].vectY, vectChoqX, vectChoqY,
                         modCuadVectChoq, indice);
        almas [indice].vectX += pala.getVectX ();
        hayChoque = true;
        partida.suena (Sonido.BOLA_IMPACTA);
      }
    }

    // Si la bola está imantada y hay choque, la bola queda pegada a la pala.
    if (hayChoque && pala.estaImantada ())
    {
      almas[indice].coordY = 510 - diam;
      almas[indice].pegadaPala = true;
    }
  }

  /** En esta función se comprueba si una bola ha chocado con otra; si lo ha hecho, calcula
      la nueva velocidad y dirección de las dos pelotas.
      @param indice byte  Índice de la bola en cuestión. */
  private void choquePelota (byte indice)
  {
    for (byte i = 0; i < numAlmsAnim; i++)
    {
      // El bucle se salta una vuelta cuando la otra bola es la misma.
      if (indice == i)
        continue;

      // Primero miramos si el 'clip' de las dos bolas se superponen.
      float vectChoqX = almas [indice].coordX - almas [i].coordX;
      float vectChoqY = almas [indice].coordY - almas [i].coordY;
      if (((vectChoqX < 16) && (vectChoqX > - 16)) || ((vectChoqY < 16) && (vectChoqY > - 16)))
      {
        float modVectCuad = (float) (Math.pow (almas [indice].coordX - almas [i].coordX, 2) +
                                     Math.pow (almas [indice].coordY - almas [i].coordY, 2));
        // Miramos si las dos bolas están demasiado juntas.
        if (modVectCuad < Math.pow (diam, 2))
        {
          // Sólo hay choque si una se acerca a la otra.
          if (((almas [indice].vectX * vectChoqX + almas [indice].vectY * vectChoqY) -
               (almas [i].vectX * vectChoqX + almas [i].vectY * vectChoqY)) < 0)
          {
            float nuevoVectXBola = (almas [i].vectX * vectChoqX * vectChoqX +
                                    almas [i].vectY * vectChoqX * vectChoqY +
                                    almas [indice].vectX * vectChoqY * vectChoqY -
                                    almas [indice].vectY * vectChoqX * vectChoqY) / modVectCuad;
            float nuevoVectYBola = (almas [i].vectX * vectChoqX * vectChoqY +
                                    almas [i].vectY * vectChoqY * vectChoqY -
                                    almas [indice].vectX * vectChoqX * vectChoqY +
                                    almas [indice].vectY * vectChoqX * vectChoqX) / modVectCuad;
            float nuevoVectXOtrBola = (almas [indice].vectX * vectChoqX * vectChoqX +
                                       almas [indice].vectY * vectChoqX * vectChoqY +
                                       almas [i].vectX * vectChoqY * vectChoqY -
                                       almas [i].vectY * vectChoqX * vectChoqY) / modVectCuad;
            float nuevoVectYOtrBola = (almas [indice].vectX * vectChoqX * vectChoqY +
                                       almas [indice].vectY * vectChoqY * vectChoqY -
                                       almas [i].vectX * vectChoqX * vectChoqY +
                                       almas [i].vectY * vectChoqX * vectChoqX) / modVectCuad;
            almas [indice].setVect (nuevoVectXBola, nuevoVectYBola);
            almas [i].setVect (nuevoVectXOtrBola, nuevoVectYOtrBola);
            partida.suena (Sonido.BOLA_IMPACTA);    // Efecto sonoro.
          }
        }
      }
    }
  }

  /** Comprueba si una bola ha chocado con algún alien. En tal caso, mata al alien, y
      cambia la bola de dirección.
      @param indice byte  Índice de la bola en cuestión. */
  private void choqueAlien (byte indice)
  {
    // Máxima distancia entre el centro de la bola y el del alien para que
    // los clips de sus imágenes se superpongan.
    byte maxSep = (byte) (rad + aliens.getRad ());
    // Hay que comprobar todos los aliens en pantalla.
    byte numAliens = aliens.getNumAliensAnim ();
    for (byte i = 0; i < numAliens; i++)
    {
      // Si el alien está muerto, segimos.
      if (!aliens.estaVivo (i))
        continue;

      // Primero miramos si el 'clip' de la bola y el alien se superponen.
      float vectChoqX = (almas [indice].coordX + rad) - (aliens.getCoordX (i) + aliens.getRad ());
      float vectChoqY = (almas [indice].coordY + rad) - (aliens.getCoordY (i) + aliens.getRad ());
      if (((vectChoqX < maxSep) && (vectChoqX > -maxSep))
         || ((vectChoqY < maxSep) && (vectChoqY > -maxSep)))
      {
        float modVectCuad = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
        // Miramos si la bola y el alien están demasiado juntos.
        if (modVectCuad < Math.pow (maxSep, 2))
        {
          float nuevoVectXBola = (aliens.getVectX (i) * vectChoqX * vectChoqX +
                                  aliens.getVectY (i) * vectChoqX * vectChoqY +
                                  almas [indice].vectX * vectChoqY * vectChoqY -
                                  almas [indice].vectY * vectChoqX * vectChoqY) / modVectCuad;
          float nuevoVectYBola = (aliens.getVectX (i) * vectChoqX * vectChoqY +
                                  aliens.getVectY (i) * vectChoqY * vectChoqY -
                                  almas [indice].vectX * vectChoqX * vectChoqY +
                                  almas [indice].vectY * vectChoqX * vectChoqX) / modVectCuad;
          almas [indice].setVect (nuevoVectXBola, nuevoVectYBola);
          // Matamos al alien, sumamos los puntos, y suena el ruidillo.
          aliens.mataAlien (i);
          partida.sumaPuntos (500);
          partida.suena (Sonido.BOLA_IMPACTA);
        }
      }
    }
  }

  /** Comprueba si una bola ha chocado con algún tiro. En tal caso, mata el tiro, y
      cambia la bola de dirección.
      @param indice byte  Índice de la bola en cuestión. */
  private void choqueTiro (byte indice)
  {
    // Máxima distancia entre el centro de la bola y el del tiro para que
    // los clips de sus imágenes se superpongan.
    byte maxSep = (byte) (rad + 3);
    // Hay que comprobar todos los tiros en pantalla.
    byte numTiros = pala.getNumTirosAnim ();
    for (byte i = 0; i < numTiros; i++)
    {
      // Si el tiro ya no está activo, segimos.
      if (!pala.tiroEstaActivo (i))
        continue;

      // Primero miramos si el 'clip' de la bola y el tiro se superponen.
      float vectChoqX = (almas [indice].coordX + rad) - (pala.getCoordXTiro (i) + 3);
      float vectChoqY = (almas [indice].coordY + rad) - (pala.getCoordYTiro (i) + 3);
      if (((vectChoqX < maxSep) && (vectChoqX > -maxSep))
         || ((vectChoqY < maxSep) && (vectChoqY > -maxSep)))
      {
        // Miramos si la bola y el tiro están demasiado juntos.
        float modVectCuad = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
        if (modVectCuad < Math.pow (maxSep, 2))
        {
          float nuevoVectXBola = (-6 * vectChoqX * vectChoqY +
                                  almas [indice].vectX * vectChoqY * vectChoqY -
                                  almas [indice].vectY * vectChoqX * vectChoqY) / modVectCuad;
          float nuevoVectYBola = (-6 * vectChoqY * vectChoqY -
                                  almas [indice].vectX * vectChoqX * vectChoqY +
                                  almas [indice].vectY * vectChoqX * vectChoqX) / modVectCuad;
          almas [indice].setVect (nuevoVectXBola, nuevoVectYBola);
          // Matamos el tiro, y hacemos sonar el ruidito.
          pala.mataTiro (i);
          partida.suena (Sonido.BOLA_IMPACTA);
        }
      }
    }
  }

  /** Comprueba si una bola choca contra una de las paredes del escenario.
      Si es así, ajusta el vector de movimiento de la bola, y reproduce el efecto sonoro.
      Si la bola choca con el suelo, muere.
      @param indice byte  Índice de la bola en cuestión. */
  private void choquePared (byte indice)
  {
    // ¿Ha chocado con el lado izquierdo?
    if (almas [indice].coordX < 30)
    {
      almas [indice].coordX = (short) (2 * 30 - almas [indice].coordX);
      almas [indice].vectX *= -1;
      partida.suena (Sonido.BOLA_IMPACTA);
    }
    // ¿Ha chocado con el lado derecho?
    if (almas [indice].coordX + diam > 470)
    {
      almas [indice].coordX = (short) (2 * (470 - diam) - almas [indice].coordX);
      almas [indice].vectX *= -1;
      partida.suena (Sonido.BOLA_IMPACTA);
    }
    // ¿Ha chocado con el techo?
    if (almas [indice].coordY < 40)
    {
      almas [indice].coordY = (short) (2 * 40 - almas [indice].coordY);
      almas [indice].vectY *= -1;
      partida.suena (Sonido.BOLA_IMPACTA);
    }
    // ¿Ha chocado con el suelo? De ser así, debe morir.
    if (almas [indice].coordY  + diam > 520)
      mataBola (indice);
  }

  /** Comprueba si una bola ha chocado con algún ladrillo. En tal caso, la cambia de dirección,
      y se le notifica al ladrillo el impacto.
      @param indice byte  Índice de la bola en cuestión. */
  private void choqueLadrillo (byte indice)
  {
    // Según la dirección y el sentido de la bola, el choque se podría dar por diferentes puntos del
    // ladrillo.
    if (almas [indice].vectX == 0)
    {
      if (almas [indice].vectY > 0)
      {
        if (choqLadAbajo (indice) || choqLadAbajoIzq (indice) || choqLadAbajoDer (indice))
        {
          partida.suena (Sonido.BOLA_IMPACTA);
          return;
        }
      }
      else
      {
        if (choqLadArriba (indice) || choqLadArribaIzq (indice) || choqLadArribaDer (indice))
        {
          partida.suena (Sonido.BOLA_IMPACTA);
          return;
        }
      }
    }
    else if (almas [indice].vectY == 0)
    {
      if (almas [indice].vectX > 0)
      {
        if (choqLadDerecha (indice) || choqLadArribaDer (indice) || choqLadAbajoDer (indice))
        {
          partida.suena (Sonido.BOLA_IMPACTA);
          return;
        }
      }
      else
      {
        if (choqLadIzquierda (indice) || choqLadArribaIzq (indice) || choqLadAbajoIzq (indice))
        {
          partida.suena (Sonido.BOLA_IMPACTA);
          return;
        }
      }
    }
    else if (almas [indice].vectX > 0)
    {
      if (almas [indice].vectY > 0)
      {
        if (choqLadAbajoDer (indice) || choqLadAbajo (indice) || choqLadDerecha (indice)
            || choqLadArribaDer (indice) || choqLadAbajoIzq (indice))
        {
          partida.suena (Sonido.BOLA_IMPACTA);
          return;
        }
      }
      else
      {
        if (choqLadArribaDer (indice) || choqLadArriba (indice) || choqLadDerecha (indice)
            || choqLadArribaIzq (indice) || choqLadAbajoDer (indice))
        {
          partida.suena (Sonido.BOLA_IMPACTA);
          return;
        }
      }
    }
    else
    {
      if (almas [indice].vectY > 0)
      {
        if (choqLadAbajoIzq (indice) || choqLadAbajo (indice) || choqLadIzquierda (indice)
            || choqLadArribaIzq (indice) || choqLadAbajoDer (indice))
        {
          partida.suena (Sonido.BOLA_IMPACTA);
          return;
        }
      }
      else
      {
        if (choqLadArribaIzq (indice) || choqLadArriba (indice) || choqLadIzquierda (indice)
            || choqLadArribaDer (indice) || choqLadAbajoIzq (indice))
        {
          partida.suena (Sonido.BOLA_IMPACTA);
          return;
        }
      }
    }
  }

  /** Comprueba si una bola ha chocado contra la parte de arriba del ladrillo,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se avisa al ladrillo del impacto, y se ajusta el vector de movimiento de la bola.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola haya chocado o no. */
  private boolean choqLadArriba (byte indice)
  {
    // Lo primero es calcular las coordenadas del ladrillo que se encuentra bajo la bola.
    float posLadX = (almas [indice].coordX + rad - 30) / 40;
    float posLadY = (almas [indice].coordY - 100) / 20;
    // Miramos si el ladrillo podría existir (el muro puede llegar a tener 11 ladrillos de ancho
    // por 13 de alto), y luego si hay ladrillo o no en ese sitio.
    if ((posLadX >= 0) && (posLadX < 11) && (posLadY >= 0) && (posLadY < 13)
        && (nivel.getResistLadrillo ((byte) posLadX, (byte) posLadY) > 0))
    {
      nivel.impactaLadrillo ((byte) posLadX, (byte) posLadY, 1);
      almas [indice].vectY *= -1;
      return true;
    }
    // No ha habido choque.
    return false;
    // El resto de funciones que miran si hubo choque contra un ladrillo, tienen una mecánica idéntica.
  }

  /** Comprueba si una bola ha chocado contra el ángulo superior izquierdo del ladrillo,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se avisa al ladrillo del impacto, y se ajusta el vector de movimiento de la bola.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola haya chocado o no. */
  private boolean choqLadArribaIzq (byte indice)
  {
    short rCuad = (short) (Math.pow (rad, 2));
    float posLadX = (almas [indice].coordX - 30) / 40;
    float posLadY = (almas [indice].coordY - 100) / 20;
    if ((posLadX >= 0) && (posLadX < 11) && (posLadY >= 0) && (posLadY < 13)
        && (nivel.getResistLadrillo ((byte) posLadX, (byte) posLadY) > 0))
    {
      float vectChoqX = (almas [indice].coordX + rad - 30) - ((byte) (posLadX + 1) * 40 - 1);
      float vectChoqY = (almas [indice].coordY + rad - 100) - ((byte) (posLadY + 1) * 20 - 1);
      float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
      if (modCuadVectChoq < rCuad)
      {
        nivel.impactaLadrillo ((byte) posLadX, (byte) posLadY, 1);
        ajustaVector (almas [indice].vectX, almas [indice].vectY, vectChoqX, vectChoqY,
                      modCuadVectChoq, indice);
        return true;
      }
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el ángulo superior derecho del ladrillo,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se avisa al ladrillo del impacto, y se ajusta el vector de movimiento de la bola.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola haya chocado o no. */
  private boolean choqLadArribaDer (byte indice)
  {
    short rCuad = (short) (Math.pow (rad, 2));
    float posLadX = (almas [indice].coordX + diam - 30) / 40;
    float posLadY = (almas [indice].coordY - 100) / 20;
    if ((posLadX >= 0) && (posLadX < 11) && (posLadY >= 0) && (posLadY < 13)
        && (nivel.getResistLadrillo ((byte) posLadX, (byte) posLadY) > 0))
    {
      float vectChoqX = (almas [indice].coordX + rad - 30) - ((byte) (posLadX) * 40 + 1);
      float vectChoqY = (almas [indice].coordY + rad - 100) - ((byte) (posLadY + 1) * 20 - 1);
      float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
      if (modCuadVectChoq < rCuad)
      {
        nivel.impactaLadrillo ((byte) posLadX, (byte) posLadY, 1);
        ajustaVector (almas [indice].vectX, almas [indice].vectY, vectChoqX, vectChoqY,
                      modCuadVectChoq, indice);
        return true;
      }
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra la base del ladrillo,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se avisa al ladrillo del impacto, y se ajusta el vector de movimiento de la bola.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola haya chocado o no. */
  private boolean choqLadAbajo (byte indice)
  {
    float posLadX = (almas [indice].coordX + rad - 30) / 40;
    float posLadY = (almas [indice].coordY + diam - 100) / 20;
    if ((posLadX >= 0) && (posLadX < 11) && (posLadY >= 0) && (posLadY < 13)
        && (nivel.getResistLadrillo ((byte) posLadX, (byte) posLadY) > 0))
    {
      nivel.impactaLadrillo ((byte) posLadX, (byte) posLadY, 1);
      almas [indice].vectY *= -1;
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el lado izquierdo del ladrillo,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se avisa al ladrillo del impacto, y se ajusta el vector de movimiento de la bola.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola haya chocado o no. */
  private boolean choqLadIzquierda (byte indice)
  {
    float posLadX = (almas [indice].coordX - 30) / 40;
    float posLadY = (almas [indice].coordY + rad - 100) / 20;
    if ((posLadX >= 0) && (posLadX < 11) && (posLadY >= 0) && (posLadY < 13)
        && (nivel.getResistLadrillo ((byte) posLadX, (byte) posLadY) > 0))
    {
      nivel.impactaLadrillo ((byte) posLadX, (byte) posLadY, 1);
      almas [indice].vectX *= -1;
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el lado derecho del ladrillo,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se avisa al ladrillo del impacto, y se ajusta el vector de movimiento de la bola.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola haya chocado o no. */
  private boolean choqLadDerecha (byte indice)
  {
    float posLadX = (almas [indice].coordX + diam - 30) / 40;
    float posLadY = (almas [indice].coordY + rad - 100) / 20;
    if ((posLadX >= 0) && (posLadX < 11) && (posLadY >= 0) && (posLadY < 13)
        && (nivel.getResistLadrillo ((byte) posLadX, (byte) posLadY) > 0))
    {
      nivel.impactaLadrillo ((byte) posLadX, (byte) posLadY, 1);
      almas [indice].vectX *= -1;
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el ángulo inferior izquierdo del ladrillo,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se avisa al ladrillo del impacto, y se ajusta el vector de movimiento de la bola.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola haya chocado o no. */
  private boolean choqLadAbajoIzq (byte indice)
  {
    short rCuad = (short) (Math.pow (rad, 2));
    float posLadX = (almas [indice].coordX - 30) / 40;
    float posLadY = (almas [indice].coordY + diam - 100) / 20;
    if ((posLadX >= 0) && (posLadX < 11) && (posLadY >= 0) && (posLadY < 13)
        && (nivel.getResistLadrillo ((byte) posLadX, (byte) posLadY) > 0))
    {
      float vectChoqX = (almas [indice].coordX + rad - 30) - ((byte) (posLadX + 1) * 40 - 1);
      float vectChoqY = (almas [indice].coordY + rad - 100) - ((byte) (posLadY) * 20 + 1);
      float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
      if (modCuadVectChoq < rCuad)
      {
        nivel.impactaLadrillo ((byte) posLadX, (byte) posLadY, 1);
        ajustaVector (almas [indice].vectX, almas [indice].vectY, vectChoqX, vectChoqY,
                      modCuadVectChoq, indice);
        return true;
      }
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el ángulo inferior derecho del ladrillo,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se avisa al ladrillo del impacto, y se ajusta el vector de movimiento de la bola.
      @param indice byte  Índice de la bola en cuestión.
      @return boolean     'true' o 'false' según la bola haya chocado o no. */
  private boolean choqLadAbajoDer (byte indice)
  {
    short rCuad = (short) (Math.pow (rad, 2));
    float posLadX = (almas [indice].coordX + diam - 30) / 40;
    float posLadY = (almas [indice].coordY + diam - 100) / 20;
    if ((posLadX >= 0) && (posLadX < 11) && (posLadY >= 0) && (posLadY < 13)
        && (nivel.getResistLadrillo ((byte) posLadX, (byte) posLadY) > 0))
    {
      float vectChoqX = (almas [indice].coordX + rad - 30) - ((byte) (posLadX) * 40 + 1);
      float vectChoqY = (almas [indice].coordY + rad - 100) - ((byte) (posLadY) * 20 + 1);
      float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
      if (modCuadVectChoq < rCuad)
      {
        nivel.impactaLadrillo ((byte) posLadX, (byte) posLadY, 1);
        ajustaVector (almas [indice].vectX, almas [indice].vectY, vectChoqX, vectChoqY,
                      modCuadVectChoq, indice);
        return true;
      }
    }
    return false;
  }

  /** Ajusta el vector de movimiento de una bola. Se utiliza cuando la bola ha chocado con
      alguno de los ángulos de un ladrillo, o un bono.
      @param vectReboteX float      El componente X del vector de movimiento de la bola.
      @param vectReboteY float      El componente Y del vector de movimiento de la bola.
      @param vectChoqX float        El componente X del vector de choque.
      @param vectChoqY float        El componente Y del vector de choque.
      @param modCuadVectChoq float  El módulo del vector de choque al cuadrado.
      @param indice byte            El índice de la bola en cuestión. */
  private void ajustaVector (float vectReboteX, float vectReboteY, 
                             float vectChoqX, float vectChoqY,
                             float modCuadVectChoq, byte indice)
  {
    float nuevoVectX = ((-vectReboteX) * vectChoqX * vectChoqX +
                        (-vectReboteY) * vectChoqX * vectChoqY +
                        vectReboteX * vectChoqY * vectChoqY -
                        vectReboteY * vectChoqX * vectChoqY) / modCuadVectChoq;
    float nuevoVectY = ((-vectReboteX) * vectChoqX * vectChoqY +
                        (-vectReboteY) * vectChoqY * vectChoqY +
                        vectReboteX * vectChoqX * vectChoqY -
                        vectReboteY * vectChoqX * vectChoqX) / modCuadVectChoq;
    almas [indice].setVect (nuevoVectX, nuevoVectY);
  }

  /** Ajusta el vector de movimiento de una bola. Se utiliza cuando la bola ha chocado con
      alguno de los ángulos de un ladrillo, o un bono.
      @param vectReboteX float      El componente X del vector de movimiento de la bola multiplicado por -1.
      @param vectReboteY float      El componente Y del vector de movimiento de la bola por -1.
      @param vectChoqX float        El componente X del vector de choque.
      @param vectChoqY float        El componente Y del vector de choque.
      @param modCuadVectChoq float  El módulo del vector de choque al cuadrado.
      @param indice byte            El índice de la bola en cuestión. */
  private void ajstVectChoqBrd (float vectReboteX, float vectReboteY,
                                float vectChoqX, float vectChoqY, float modCuadVectChoq, 
                                byte indice)
  {
    float nuevoVectX = (vectReboteX * vectChoqX * vectChoqX +
                        vectReboteY * vectChoqX * vectChoqY) / modCuadVectChoq;
    float nuevoVectY = (vectReboteX * vectChoqX * vectChoqY +
                        vectReboteY * vectChoqY * vectChoqY) / modCuadVectChoq;
    almas [indice].setVect (nuevoVectX, nuevoVectY);
  }

  /** Comprueba si una bola ha chocado con algún bono. En tal caso, la cambia de dirección
      y destruye el bono.
      @param indice byte  Índice de la bola en cuestión. */
  private void choqueBono (byte indice)
  {
    // Hay que comprobar todos los bonos en pantalla.
    byte numBonos = bono.getNumBonosAnim ();
    for (byte i = 0; i < numBonos; i++)
    {
      // Si el bono está estallando, seguimos.
      if (!bono.estaActivo (i))
        continue;
      // Igual que con los ladrillos, según la dirección y el sentido de la bola,
      // el choque se podría dar por diferentes puntos del bono.
      if (almas [indice].vectX == 0)
      {
        if (almas [indice].vectY > 0)
        {
          if (choqBonoAbajo (indice, i) || choqBonoAbajoIzq (indice, i) || choqBonoAbajoDer (indice, i))
          {
            partida.suena (Sonido.BOLA_IMPACTA);
            return;
          }
        }
        else
        {
          if (choqBonoArriba (indice, i) || choqBonoArribaIzq (indice, i) || choqBonoArribaDer (indice, i))
          {
            partida.suena (Sonido.BOLA_IMPACTA);
            return;
          }
        }
      }
      else if (almas [indice].vectY == 0)
      {
        if (almas [indice].vectX > 0)
        {
          if (choqBonoDerecha (indice, i) || choqBonoArribaDer (indice, i) || choqBonoAbajoDer (indice, i))
          {
            partida.suena (Sonido.BOLA_IMPACTA);
            return;
          }
        }
        else
        {
          if (choqBonoIzquierda (indice, i) || choqBonoArribaIzq (indice, i) || choqBonoAbajoIzq (indice, i))
          {
            partida.suena (Sonido.BOLA_IMPACTA);
            return;
          }
        }
      }
      else if (almas [indice].vectX > 0)
      {
        if (almas [indice].vectY > 0)
        {
          if (choqBonoAbajoDer (indice, i) || choqBonoAbajo (indice, i) || choqBonoDerecha (indice, i)
              || choqBonoArribaDer (indice, i) || choqBonoAbajoIzq (indice, i))
          {
            partida.suena (Sonido.BOLA_IMPACTA);
            return;
          }
        }
        else
        {
          if (choqBonoArribaDer (indice, i) || choqBonoArriba (indice, i) || choqBonoDerecha (indice, i)
              || choqBonoArribaIzq (indice, i) || choqBonoAbajoDer (indice, i))
          {
            partida.suena (Sonido.BOLA_IMPACTA);
            return;
          }
        }
      }
      else
      {
        if (almas [indice].vectY > 0)
        {
          if (choqBonoAbajoIzq (indice, i) || choqBonoAbajo (indice, i) || choqBonoIzquierda (indice, i)
              || choqBonoArribaIzq (indice, i) || choqBonoAbajoDer (indice, i))
          {
            partida.suena (Sonido.BOLA_IMPACTA);
            return;
          }
        }
        else
        {
          if (choqBonoArribaIzq (indice, i) || choqBonoArriba (indice, i) || choqBonoIzquierda (indice, i)
              || choqBonoArribaDer (indice, i) || choqBonoAbajoIzq (indice, i))
          {
            partida.suena (Sonido.BOLA_IMPACTA);
            return;
          }
        }
      }
    }
  }

  /** Comprueba si una bola ha chocado contra el lado de arriba de un bono,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se ajusta el vector de movimiento de la bola, y se 'mata' el bono.
      @param indAlma byte  Índice de la bola en cuestión.
      @param indBono byte  Índice del bono de marras.
      @return boolean      'true' o 'false' según la bola haya chocado o no. */
  private boolean choqBonoArriba (byte indAlma, byte indBono)
  {
    if ((almas [indAlma].coordX  + rad > bono.getCoordX (indBono))
          && (almas [indAlma].coordX + rad < bono.getCoordX (indBono) + 30)
          && (almas [indAlma].coordY < bono.getCoordY (indBono) + 10)
          && (almas [indAlma].coordY > bono.getCoordY (indBono)))
    {
      bono.mataBono (indBono);
      almas [indAlma].vectY *= -1;
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el ángulo superior izquierdo de un bono,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se ajusta el vector de movimiento de la bola, y se 'mata' el bono.
      @param indAlma byte  Índice de la bola en cuestión.
      @param indBono byte  Índice del bono de marras.
      @return boolean      'true' o 'false' según la bola haya chocado o no. */
  private boolean choqBonoArribaIzq (byte indAlma, byte indBono)
  {
    short rCuad = (short) (Math.pow (rad, 2));
    float vectChoqX = (almas [indAlma].coordX + rad) - (bono.getCoordX (indBono) + 30);
    float vectChoqY = (almas [indAlma].coordY + rad) - (bono.getCoordY (indBono) + 10);
    float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
    if (modCuadVectChoq < rCuad)
    {
      bono.mataBono (indBono);
      ajustaVector (almas [indAlma].vectX, almas [indAlma].vectY, vectChoqX, vectChoqY,
                    modCuadVectChoq, indAlma);
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el ángulo superior derecho de un bono,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se ajusta el vector de movimiento de la bola, y se 'mata' el bono.
      @param indAlma byte  Índice de la bola en cuestión.
      @param indBono byte  Índice del bono de marras.
      @return boolean      'true' o 'false' según la bola haya chocado o no. */
  private boolean choqBonoArribaDer (byte indAlma, byte indBono)
  {
    short rCuad = (short) (Math.pow (rad, 2));
    float vectChoqX = (almas [indAlma].coordX + rad) - (bono.getCoordX (indBono));
    float vectChoqY = (almas [indAlma].coordY + rad) - (bono.getCoordY (indBono) + 10);
    float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
    if (modCuadVectChoq < rCuad)
    {
      bono.mataBono (indBono);
      ajustaVector (almas [indAlma].vectX, almas [indAlma].vectY, vectChoqX, vectChoqY,
                    modCuadVectChoq, indAlma);
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra la base de un bono,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se ajusta el vector de movimiento de la bola, y se 'mata' el bono.
      @param indAlma byte  Índice de la bola en cuestión.
      @param indBono byte  Índice del bono de marras.
      @return boolean      'true' o 'false' según la bola haya chocado o no. */
  private boolean choqBonoAbajo (byte indAlma, byte indBono)
  {
    if ((almas [indAlma].coordX  + rad > bono.getCoordX (indBono))
          && (almas [indAlma].coordX + rad < bono.getCoordX (indBono) + 30)
          && (almas [indAlma].coordY + diam > bono.getCoordY (indBono))
          && (almas [indAlma].coordY + diam < bono.getCoordY (indBono) + 10))
    {
      bono.mataBono (indBono);
      almas [indAlma].vectY *= -1;
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el lado izquierdo de un bono,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se ajusta el vector de movimiento de la bola, y se 'mata' el bono.
      @param indAlma byte  Índice de la bola en cuestión.
      @param indBono byte  Índice del bono de marras.
      @return boolean      'true' o 'false' según la bola haya chocado o no. */
  private boolean choqBonoIzquierda (byte indAlma, byte indBono)
  {
    if ((almas [indAlma].coordY + rad > bono.getCoordY (indBono))
         && (almas [indAlma].coordY + rad < bono.getCoordY (indBono) + 10)
         && (almas [indAlma].coordX < bono.getCoordX (indBono) + 30)
         && (almas [indAlma].coordX > bono.getCoordX (indBono)))
    {
      bono.mataBono (indBono);
      almas [indAlma].vectX *= -1;
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el lado derecho de un bono,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se ajusta el vector de movimiento de la bola, y se 'mata' el bono.
      @param indAlma byte  Índice de la bola en cuestión.
      @param indBono byte  Índice del bono de marras.
      @return boolean      'true' o 'false' según la bola haya chocado o no. */
  private boolean choqBonoDerecha (byte indAlma, byte indBono)
  {
    if ((almas [indAlma].coordY + rad > bono.getCoordY (indBono))
          && (almas [indAlma].coordY + rad < bono.getCoordY (indBono) + 10)
          && (almas [indAlma].coordX > bono.getCoordX (indBono))
          && (almas [indAlma].coordX < bono.getCoordX (indBono) + 30))
    {
      bono.mataBono (indBono);
      almas [indAlma].vectX *= -1;
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el ángulo inferior izquierdo de un bono,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se ajusta el vector de movimiento de la bola, y se 'mata' el bono.
      @param indAlma byte  Índice de la bola en cuestión.
      @param indBono byte  Índice del bono de marras.
      @return boolean      'true' o 'false' según la bola haya chocado o no. */
  private boolean choqBonoAbajoIzq (byte indAlma, byte indBono)
  {
    short rCuad = (short) (Math.pow (rad, 2));
    float vectChoqX = (almas [indAlma].coordX + rad) - (bono.getCoordX (indBono) + 30);
    float vectChoqY = (almas [indAlma].coordY + rad) - (bono.getCoordY (indBono));
    float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
    if (modCuadVectChoq < rCuad)
    {
      bono.mataBono (indBono);
      ajustaVector (almas [indAlma].vectX, almas [indAlma].vectY, vectChoqX, vectChoqY,
                    modCuadVectChoq, indAlma);
      return true;
    }
    return false;
  }

  /** Comprueba si una bola ha chocado contra el ángulo inferior derecho de un bono,
      y devuelve un valor booleano según lo averiguado.
      Si hay choque, se ajusta el vector de movimiento de la bola, y se 'mata' el bono.
      @param indAlma byte  Índice de la bola en cuestión.
      @param indBono byte  Índice del bono de marras.
      @return boolean      'true' o 'false' según la bola haya chocado o no. */
  private boolean choqBonoAbajoDer (byte indAlma, byte indBono)
  {
    short rCuad = (short) (Math.pow (rad, 2));
    float vectChoqX = (almas [indAlma].coordX + rad) - (bono.getCoordX (indBono));
    float vectChoqY = (almas [indAlma].coordY + rad) - (bono.getCoordY (indBono));
    float modCuadVectChoq = (float) (Math.pow (vectChoqX, 2) + Math.pow (vectChoqY, 2));
    if (modCuadVectChoq < rCuad)
    {
      bono.mataBono (indBono);
      ajustaVector (almas [indAlma].vectX, almas [indAlma].vectY, vectChoqX, vectChoqY,
                    modCuadVectChoq, indAlma);
      return true;
    }
    return false;
  }

  /** Clase interna que contiene la información de una bola individual. Su constructor, sus funciones y 
sus variables son privadas, ya que sólo debe acceder a ellas la misma clase 'Bola'. */
private class AlmaBola { // Las coordenadas de la bola en la pantalla. private float coordX; private float coordY; // Componentes del vector de movimiento de la bola. private float vectX; private float vectY; // Si la bola está viva, y si está pegada a la pala. private boolean vive; private boolean pegadaPala; // 'Alarma' para programar algunos sucesos. private java.util.Timer alarma; // Índices de la secuencia de animación de la bola cuando está muriendo, // y cuando está cambiando de tamaño. private byte indiceAnimMuere; private byte indiceAnimMuereGrande; private byte indiceAnimCambiaTamano; /** Constructor. */ private AlmaBola () { coordX = coordY = vectX = vectY = indiceAnimMuere = indiceAnimMuereGrande = indiceAnimCambiaTamano = 0; vive = true; pegadaPala = false; alarma = new java.util.Timer (); } /** Define las coordenadas de la bola. @param nuevaCoordX float Nueva coordenada X de la bola. @param nuevaCoordY float Nueva coordenada Y de la bola. */ private void setCoords (float nuevaCoordX, float nuevaCoordY) { coordX = nuevaCoordX; coordY = nuevaCoordY; } /** Define los componentes del vector de movimiento de la bola. @param nuevoVectX float Nuevo componente X del vector de movimiento de la bola. @param nuevoVectY float Nuevo componente Y del vector de movimiento de la bola. */ private void setVect (float nuevoVectX, float nuevoVectY) { vectX = nuevoVectX; vectY = nuevoVectY; } } }

¿Comentarios, sugerencias?: llopsite.at.yahoo.es | © 2005-07 Albert Lobo

Última actualización: 18-Feb-2007

Hosted by www.Geocities.ws

1