ArkaLlop - Clon del Arkanoid
Llop Site Home > JAVA > ArkaLlop > Pantalla de juego
Pantalla de juego:
Clase PantallaJuego package arkallop; import java.lang.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** 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 extiende 'JPanel', y que sirve como pantalla de juego. */ public class PantallaJuego extends JPanel implements Runnable, MouseListener, MouseMotionListener, KeyListener { // Hilo que se encargará del refresco constante del panel. private Thread hilo; // Variable que decide si el panel debe refrescarse constantemente o no. private boolean rfrCns; // Los 'sprites' que intervendrán en el juego. private Pala pala; private Bola bola; private Aliens aliens; private Bonos bonos; // Los 'niveles' o escenarios. private Nivel nivel; // Esta partida. private Partida partida; // La fuente y los bordes de la pantalla. private Font fuente; private BordesPantalla bordes; // Las etiquetas para la información de la partida: // la puntuación, la cuenta de los escenarios completados, el total de escenarios a completar, // el escenario actual, y el número de vidas que quedan. private ArkaLabel etqPunt; private ArkaLabel etqNivAct; private ArkaLabel etqTotNiv; private ArkaLabel etqEsc; private ArkaLabel etqVds; // Botón que permitirá volver de la pantalla de juego a la de menú. private ArkaButton botonFin; // Cuándo se disparó el último tiro -no queremos demasiados tiros de golpe. private long ultimoTiroTimeStamp; // Un array de letras, y su índice. Para detectar los trucos, que se entran por el teclado. private byte indiceLetraTrucoActual; private char trucoInput []; // Variables para conocer: si la pala se mueve, y cuánto se ha movido (conociendo dónde estaba). private boolean rtnPls; private int ultXRtn; // Contexto gráfico que se utilizará para pintar los 'sprites'. Esta clase permite renderizar la // imagen usando un suavizado (anti-aliasing), que mejora el efecto de animación. private Graphics2D graficos; /** Constructor. Sólo necesita que se le pase una instancia de la partida actual. @param nuevaPartida Partida Esta partida. */ public PantallaJuego (Partida nuevaPartida) { // Llamamos al constructor de 'JPanel', // lo hacemos 'enfocable' (para que detecte eventos, por ejemplo de teclado y de ratón), // le asignamos un gestor de diseño nulo (para poder ubicar cada componente en su sitio), // y le ponemos un cursor personalizado. super (); setFocusable (true); setLayout (null); setCursor (Toolkit.getDefaultToolkit ().createCustomCursor (ArkaLlop.getImagen ("cursor.gif"), new Point (16, 16), "CursorLlop")); // Inicicaliza las variables y los componentes. partida = nuevaPartida; inicializaVariables (); inicializaComponentes (); // Añade los oyentes. addKeyListener (this); addMouseListener (this); addMouseMotionListener (this); // Arranca los hilos de los 'sprites' y el de la pantalla misma. partida.arrancaHilosSprites (); arrancaHilo (); } /** Inicializa las variables de clase. */ private void inicializaVariables () { // Inicializamos la fuente, y los bordes. fuente = ArkaLlop.getFuenteBeyondWonderland (); bordes = new BordesPantalla (); // Las variables para el control de la entrada de trucos. trucoInput = new char [20]; for (byte i = 0; i < trucoInput.length; i++) trucoInput [i] = ' '; indiceLetraTrucoActual = 10; // El refresco constante y el hilo. rfrCns = false; hilo = new Thread (this); // El último disparo. ultimoTiroTimeStamp = System.currentTimeMillis (); } /** Inicializa los componentes. */ private void inicializaComponentes () { // Inicializamos los 'sprites'. pala = partida.getPala (); aliens = partida.getAliens (); bola = partida.getBola (); nivel = partida.getNivel (); bonos = partida.getBonos (); // Las etiquetas. etqPunt = new ArkaLabel (); etqPunt.setLocation (10, 550); etqPunt.setSize (160, 40); etqNivAct = new ArkaLabel (); etqNivAct.setLocation (262, 520); etqNivAct.setSize (35, 40); etqTotNiv = new ArkaLabel (); etqTotNiv.setLocation (325, 520); etqTotNiv.setSize (35, 40); etqEsc = new ArkaLabel (); etqEsc.setLocation (310, 550); etqEsc.setSize (35, 40); etqVds = new ArkaLabel (); etqVds.setLocation (450, 550); etqVds.setSize (40, 40); // Añadimos las etiquetas al panel. add (etqPunt); add (etqNivAct); add (etqTotNiv); add (etqEsc); add (etqVds); // El botón para volver a la pantalla de menú. botonFin = new ArkaButton (); botonFin.setAction (new AbstractAction ("OK") { public void actionPerformed (ActionEvent evento) { remove (botonFin); partida.cambioAMenu (); } }); botonFin.setLocation (200, 250); botonFin.setSize (100, 60); } /** Añade al panel el botón que permite volver a la pantalla de menú. */ public void anadeBotonFin () { add (botonFin); botonFin.requestFocus (); } /** Añade una nueva letra al array de los trucos, y comprueba si se ha completado la introducción de un truco. @param nuevaLetra char Letra a introducir en el array. */ private void nuevaLetraTruco (char nuevaLetra) { // Si el array está lleno, hacemos hueco para la nueva letra. if (indiceLetraTrucoActual == 20) reordenaTrucoInput (); // Metemos la nueva letra. trucoInput [indiceLetraTrucoActual++] = nuevaLetra; // Comprobamos si acaba de introducirse un truco válido. // Los trucos -una palabra- tienen una longitud máxima de 10 letras; por eso, pasamos a la // función que valida los trucos una cadena con las últimas 10 letras del array 'trucoInput'. int offset = indiceLetraTrucoActual - 10; String cadenaTruco = new String (trucoInput, offset, 10); trataCadenaTruco (cadenaTruco); } /** Comprueba si una cadena termina en uno de los trucos. En caso afirmativo, se aplica el truco. @param nuevaCadenaTruco String Cadena en la que puede haber un truco. */ private void trataCadenaTruco (String nuevaCadenaTruco) { if (nuevaCadenaTruco.endsWith ("schumacher")) aplicaTruco (Bonos.ACELERA_BOLAS); else if (nuevaCadenaTruco.endsWith ("caracoles")) aplicaTruco (Bonos.DECELERA_BOLAS); else if (nuevaCadenaTruco.endsWith ("tripartito")) aplicaTruco (Bonos.TRES_BOLAS); else if (nuevaCadenaTruco.endsWith ("eastwood")) aplicaTruco (Bonos.TAMANO_BOLAS); else if (nuevaCadenaTruco.endsWith ("matanza")) aplicaTruco (Bonos.DISPAROS_PALA); else if (nuevaCadenaTruco.endsWith ("bandeja")) aplicaTruco (Bonos.ANCHURA_PALA); else if (nuevaCadenaTruco.endsWith ("cianuro")) aplicaTruco (Bonos.IMAN_PALA); else if (nuevaCadenaTruco.endsWith ("carpediem")) aplicaTruco (Bonos.VIDA); else if (nuevaCadenaTruco.endsWith ("gusano")) aplicaTruco (Bonos.SIGUIENTE_NIVEL); } /** Aplica un truco. Por ser trampa, tiene el coste de 10000 puntos. @param bono byte Truco a aplicar -una de las conastantes de la clase 'Bonos'. */ private void aplicaTruco (byte bono) { partida.suena (Sonido.TRUCO); // Suena un pedo. partida.absorbeBono (bono); partida.sumaPuntos (-10000); } /** Adelanta 10 posiciones las últimas 10 letras del array para los trucos. */ private void reordenaTrucoInput () { indiceLetraTrucoActual = 0; for (byte i = 10; i < 20; i++) { trucoInput [indiceLetraTrucoActual++] = trucoInput [i]; trucoInput [i] = ' '; } } /** Arranca el hilo que refresca el panel. */ public void arrancaHilo () { if (!hilo.isAlive ()) hilo.start (); } /** Permite saber si este panel se está refrescando constantemente. @return boolean 'true' si el refresco constante está activado, 'false' si no lo está. */ public boolean getRefrescoConstante () { return rfrCns; } /** Define el refresco constante de este panel. Si esta variable es 'true', el panel se repintará cada muy poco tiempo. De esta forma se logran efectos de animación. @param nuevoRefrescoConstante boolean Nuevo valor que tomará 'refrescoConstante'. */ public void setRefrescoConstante (boolean nuevoRefrescoConstante) { rfrCns = nuevoRefrescoConstante; } /** Función sobrecargada para pintar los bordes del panel. @param graficos Graphics Contexto 'Graphics' sobre el que pintar. */ public void paintBorder (Graphics graficos) { graficos.drawImage (BordesPantalla.getImagenBorde (BordesPantalla.BRD_SUP), 0, 0, this); graficos.drawImage (BordesPantalla.getImagenBorde (BordesPantalla.BRD_LAD), 0, 40, this); graficos.drawImage (BordesPantalla.getImagenBorde (BordesPantalla.PUERTA), 0, 480, this); graficos.drawImage (BordesPantalla.getImagenBorde (BordesPantalla.BRD_LAD), 470, 40, this); graficos.drawImage (BordesPantalla.getImagenBorde (BordesPantalla.BRD_INF), 0, 520, this); if (partida.getPuedeAvanzarNivel ()) graficos.drawImage (bordes.getPuerta (), 470, 480, this); else graficos.drawImage (BordesPantalla.getImagenBorde (BordesPantalla.PUERTA), 470, 480, this); for (byte i = 0; i < 3; i++) graficos.drawImage (bordes.getVaina(), 90 + i * 140, 0, this); } /** Método sobrecargado. Pinta la imagen de fondo y los 'sprites'. @param graficos Graphics Contexto 'Graphics' sobre el que se pinta. */ public void paintComponent (Graphics grfcs) { byte i; // El contexto 'Graphics2D' que pintará los 'sprites'. Renderizará las imágenes con 'suavizado'. graficos = (Graphics2D) grfcs; graficos.addRenderingHints (new RenderingHints (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); super.paintComponent (graficos); // Pintar la imagen de fondo, la pala, las bolas, los aliens, los bonos, los tiros, y los ladrillos. graficos.drawImage (FondosPantalla.getImagenFondo (partida.getFondoEscenario ()), 0, 0, this); graficos.drawImage (pala.getImagenPala (), pala.getCoordX (), 510, this); for (i = 0; i < bola.getNumAlmsAnim (); i++) graficos.drawImage (bola.getImagen (i), (int) bola.getCoordX (i), (int) bola.getCoordY (i), this); for (i = 0; i < aliens.getNumAliensAnim (); i++) graficos.drawImage (aliens.getImagen (i), (int) aliens.getCoordX (i), (int) aliens.getCoordY (i), this); for (i = 0; i < bonos.getNumBonosAnim (); i++) graficos.drawImage (bonos.getImagenBono (i), (int) bonos.getCoordX (i), (int) bonos.getCoordY (i), this); for (i = 0; i < pala.getNumTirosAnim (); i++) graficos.drawImage (pala.getImagenTiro (i), (int) pala.getCoordXTiro (i), (int) pala.getCoordYTiro (i), this); for (i = 0; i < 11; i++) for (byte j = 0; j < 13; j++) if (nivel.ladrilloEsVisible (i, j)) graficos.drawImage (nivel.getImagen (i, j), 30 + (40 * i), 100 + (20 * j), this); // Si la partida está terminada, se está cargando un nivel, o la partida aún no ha empezado // o está pausada, habrá que pintar un recuadro con un mensaje sobre la pantalla. if (partida.getFinalizada ()) { graficos.setColor (Color.black); graficos.fillRect (70, 200, 360, 120); graficos.setFont (fuente); graficos.setColor (Color.red); if (partida.getResultadoPartida () == Partida.DERROTA) graficos.drawString ("Game Over", 170, 235); else graficos.drawString ("Ha completado el juego!", 90, 235); } else if (partida.getCargandoNivel ()) { graficos.setColor (Color.black); graficos.fillRect (130, 240, 240, 50); graficos.setFont (fuente); graficos.setColor (Color.red); graficos.drawString ("Cargando Nivel...", 140, 275); } else if (partida.getPrePartida ()) { graficos.setColor (Color.black); graficos.fillRect (75, 240, 350, 50); graficos.setFont (fuente); graficos.setColor (Color.red); graficos.drawString ("Pulse 'Enter' para jugar.", 85, 275); } else if (partida.getPausa ()) { graficos.setColor (Color.black); graficos.fillRect (130, 240, 240, 50); graficos.setFont (fuente); graficos.setColor (Color.red); graficos.drawString ("Pausa", 200, 275); } } /** Método sobrecargado que se llama al arrancar el hilo. Ejecuta un bucle interminable que, mientras haya 'refrescoConstante', repintará la 'PantallaJuego', y se esperará 40 milésimas, para no acaparar los recurso que se dedican al programa. */ public void run () { // Variable que almacenará los milisegundos entre vuelta y vuelta del bucle. long tmpOffset = System.currentTimeMillis (); while (true) { // Mientras no se necesite el refresco constante, que duerma un poco el hilo. while (!rfrCns) try { hilo.sleep (100); } catch (InterruptedException e) {} // Escribimos el texto de las etiquetas. etqPunt.setText (String.valueOf (partida.getPuntuacion ())); etqNivAct.setText (String.valueOf (partida.getEscenarioActual () + 1)); etqTotNiv.setText (String.valueOf (partida.getTotalEscenarios ())); etqEsc.setText (partida.getNombreEscenario ()); etqVds.setText (String.valueOf (partida.getVidas () + 1)); // Repintar el panel. repaint (); // 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 40 milisegundos, así que si 'tmpOffset' es demasiado, // lo ajustamos para que el hilo duerma al menos 10 milésimas. tmpOffset = System.currentTimeMillis () - tmpOffset; if (tmpOffset > 30) tmpOffset = 30; try { Thread.sleep (40 - tmpOffset); } catch (InterruptedException e) {} tmpOffset = System.currentTimeMillis (); } } /** Método propio de la interfaz 'KeyListener'. Capta las teclas de control. @param evento KeyEvent Evento del teclado. */ public synchronized void keyPressed (KeyEvent evento) { // Si se está cargando un nivel, no hacer nada. if (partida.getCargandoNivel ()) return; // La tecla pulsada. int tcl = evento.getKeyCode (); // 'Enter' arranca la partida, y también despega las bolas que la pala ha atrapado. if (tcl == KeyEvent.VK_ENTER) { if (partida.getPrePartida ()) { partida.setPrePartida (false); partida.setPausa (!partida.getPausa ()); partida.suena (Sonido.BOLA_DESPEGA); } else if (pala.estaImantada () && !partida.getPausa ()) for (byte i = 0; i < bola.getNumAlmsAnim (); i++) if (bola.pegadaPala (i)) bola.despegaPala (i); } // 'Control' pausa y despausa el juego. else if (tcl == KeyEvent.VK_CONTROL) { if (!partida.getPrePartida ()) { if (partida.getPausa ()) { partida.suena (Sonido.PAUSA); addMouseListener (this); addMouseMotionListener (this); partida.setPausa (!partida.getPausa ()); } else { partida.setPausa (!partida.getPausa ()); removeMouseListener (this); removeMouseMotionListener (this); partida.suena (Sonido.PAUSA); } } } // 'Escape' aborta la partida. else if (tcl == KeyEvent.VK_ESCAPE) { partida.suena (Sonido.GAME_OVER); partida.terminaPartida (Partida.DERROTA); } // La barra de espacio dispara los cañones de la pala -si los hay-; // en ese caso, también puede despegar las bolas que la pala tenga atrapadas. else if (tcl == KeyEvent.VK_SPACE) { if (pala.estaArmada () && !partida.getPausa ()) { if (evento.getWhen () > ultimoTiroTimeStamp + 250) { ultimoTiroTimeStamp = evento.getWhen (); pala.dispara (); } if (pala.estaImantada ()) for (byte i = 0; i < bola.getNumAlmsAnim (); i++) if (bola.pegadaPala (i)) bola.despegaPala (i); } } } /** Método propio de la interfaz 'KeyListener'. @param evento KeyEvent Evento del teclado. */ public void keyReleased (KeyEvent evento) {} /** Método propio de la interfaz 'KeyListener'. Lleva el control de la entrada de trucos. @param evento KeyEvent Evento del teclado. */ public void keyTyped (KeyEvent evento) { char letra = evento.getKeyChar (); // Filtramos los eventos que no contengan letras. if (letra != KeyEvent.CHAR_UNDEFINED) nuevaLetraTruco (Character.toLowerCase (letra)); } /** Método para la interfaz 'MouseListener'. Pide el foco para este panel. @param e MouseEvent Evento del ratón. */ public void mouseClicked (MouseEvent e) { requestFocus (); } /** Método para la interfaz 'MouseListener'. @param e MouseEvent Evento del ratón. */ public void mouseEntered (MouseEvent e) {} /** Método para la interfaz 'MouseListener'. @param e MouseEvent Evento del ratón. */ public void mouseExited (MouseEvent e) {} /** Método para la interfaz 'MouseListener'. Mira si se ha pulsado el ratón sobre la pala o cerca de ella. En tal caso, permite mover la pala moviendo el ratón. @param e MouseEvent Evento del ratón. */ public void mousePressed (MouseEvent e) { int x = e.getX (); int y = e.getY (); if ((x >= pala.getCoordX () - 6) && (x <= pala.getCoordX () + pala.getAnchura () + 6) && (y >= 506) && (y <= 526)) { ultXRtn = x; rtnPls = true; } } /** Método para la interfaz 'MouseListener'. Al despulsar el ratón, ya no podremos mover la pala -si la teníamos agarrada. @param e MouseEvent Evento del ratón. */ public void mouseReleased (MouseEvent e) { if (rtnPls) { rtnPls = false; pala.setVectX ((short) 0); } } /** Método para la interfaz 'MouseMotionListener'. Mientras mantengamos pulsado el ratón sobre la pala, podremos mover a ésta de un lado a otro moviendo el ratón. @param e MouseEvent Evento del ratón. */ public synchronized void mouseDragged (MouseEvent e) { // El botón del ratón debe estar pulsado. if (rtnPls) { // Distancia que se ha movido el puntero. short dist = (short) (e.getX () - ultXRtn); // Según esta distancia, establecemos el vector de la pala. if (dist > 18) pala.setVectX ((short) 3); else if (dist < -18) pala.setVectX ((short) -3); else pala.setVectX (dist / 6F); // Hay que vigilar para que la pala no se introduzca -gracias a un presto golpe de ratón- en // alguna de las bolas. for (byte i = 0; i < bola.getNumAlmsAnim (); i++) { // Pasar de las bolas pegadas a la pala. if (bola.pegadaPala (i)) continue; // Pasar de las bolas que no estén lo bastante abajo en la pantalla. if (bola.getCoordY (i) + bola.getDiam () > 510) { if (bola.getCoordX (i) + bola.getRad () < pala.getCoordX ()) { // La bola está a la izquierda de la pala. if (pala.getCoordX () + dist < bola.getCoordX (i) + bola.getRad ()) { dist = (short) (bola.getCoordX (i) + bola.getVectX (i) - pala.getCoordX ()); break; } } else if (bola.getCoordX (i) > pala.getCoordX () + pala.getAnchura () - bola.getRad ()) { // La bola está a la derecha de la pala. if (pala.getCoordX () + pala.getAnchura () + dist > bola.getCoordX (i) + bola.getRad ()) { dist = (short) -(bola.getCoordX (i) + bola.getVectX (i) - (pala.getCoordX () + pala.getAnchura ())); break; } } } } // Ahora miramos que la pala no se incruste en la pared. int nuevaCoordX = pala.getCoordX () + dist; if ((nuevaCoordX >= 30) && (nuevaCoordX + pala.getAnchura () <= 470)) { pala.mueve (dist); // Movemos las bolas que estén pegadas a la pala. if (partida.getPrePartida () || pala.estaImantada ()) for (byte i = 0; i < bola.getNumAlmsAnim (); i++) if (partida.getPrePartida () || bola.pegadaPala (i)) bola.mueveHorizontalmente (i, dist); } // Si la puerta al siguiente nivel está abierta, aquí se controla si la pala la atraviesa. if ((partida.getPuedeAvanzarNivel ()) && (!partida.getCargandoNivel ()) && (nuevaCoordX + pala.getAnchura () >= 470)) partida.completaNivel (); // Actualizamos la variable para poder volver a calcular el movimiento la próxima vez. ultXRtn = e.getX (); } } /** Método para la interfaz 'MouseMotionListener'. @param e MouseEvent Evento del ratón. */ public void mouseMoved (MouseEvent e) {} }
¿Comentarios, sugerencias?: llopsite.at.yahoo.es | © 2005-07 Albert Lobo
Última actualización: 18-Feb-2007