sábado, 10 de noviembre de 2012

Cómo crear una pantalla completa para juegos en Java

votar
Supongamos que has estado aprendiendo Java durante algún tiempo; ya sabes todo lo que hay que saber sobre clases, objetos, métodos, polimorfismo, threads...Pero ahora quieres hacer algo realmente espectacular, como un juego (no para presumir, por supuesto, sólo para ampliar conocimientos). Y no un juego cualquiera, no una pequeña applet ni un juego embebido en una frame, sino un juego a pantalla completa, sin menús, ni barras horizontales o laterales que distraigan al jugador. Pero, ¿por dónde empezar?

Bueno, evidentemente, excede los límites de cualquier entrada de un blog el explicarte cómo hacer un juego en Java, pero lo que sí puedo hacer es mostrarte cómo crear una pantalla a tamaño completo para él. Antes de nada, vamos a aclarar algunos conceptos que necesitas saber (si ya los sabes, pues te vas directamente al código y listo).

Al diseñar un juego, hay que tener en cuenta que se tiene que poder ver en distintos ordenadores, de los que no vas a saber la resolución de los monitores ni la capacidad de la tarjeta de vídeo.¿Qué es la resolución? La pantalla de un monitor está dividida en pequeños puntitos llamados píxeles. El número de píxeles horizontales y verticales es la resolución de esa pantalla. Por ejemplo, un monitor con 800 píxeles horizontales y 600 píxeles verticales tiene una resolución de 800x600. Ten en cuenta que los píxeles se empiezan a contar desde 0, es decir 0 es el primer píxel y 799 el último.

Si tú pones una resolución en la pantalla de tu juego, pero el monitor tiene otra, la imagen no será tan nítida. Lo ideal es que el jugador pueda elegir la resolución según su monitor. Las resoluciones más utilizadas hoy en día son 800x600, 1024x768 y 1280x1024. En el código que verás después, hemos puesto una resolución fija, para no hacerlo muy extenso, pero tú puedes poner la de tu propio monitor si no es la misma.

Otro detalle en el que hay que fijarse, es en el Display Mode, o capacidad de bits de un monitor, que determina el número de colores que ese monitor puede mostrar. En nuestro ejemplo vamos a dejarlo en 32, que son 2 elevado a 32  colores (¿tienes una calculadora científica a mano? ¿no? bueno, son muchos colores) Si tu monitor es un poco más antiguo o prefieres aumentar la rapidez, puedes usar 16 bit, que son (esta me la sé) 65.536 colores.

Por último tenemos el Refresh Rate, que viene siendo la rapidez con la que la pantalla se está "repintando" una y otra vez. Lo que a ti te parece una imagen fija, en realidad no lo es, porque la luz de los píxeles se desvanece y el monitor está continuamente "encendiéndolos" de nuevo.

Ahora que ya hemos aclarado estos aspectos, para que sepas lo que son cuando los veas en el código, te voy a explicar lo que vamos a hacer. Vamos a crear una ventana, como las típicas de Windows, pero sin ninguna decoración, es decir, sin menús ni barras, ni botoncito de ningún tipo. En un juego normal, por supuesto, tienes la opción de salir cuando quieras, pero aquí, para favorecer la claridad del código y de lo que quiero enseñarte, vamos a dejar que esa pantalla se mantenga unos segundos y luego se desvanezca, que no es cuestión de que te quedes para siempre con ella. Si deseas que tenerla durante más o menos segundos, sólo tienes que cambiar el valor que yo le he puesto.

Hay algunos sistemas que no dejan que cambies el display que tienen por defecto, por lo que tendremos que  asegurarnos primero de que ése no es el caso. 

Así que vamos a hacer dos clases, la primera será la que tenga el constructor de la pantalla, y la segunda será la que tenga el método main, donde probaremos a ver si funciona. Con ellas crearemos una pantalla completa con un color de fondo naranja y una frase en negro, que se mantendrá durante 10 segundos.

PantallaCompleta.java

import java.awt.*;
import javax.swing.JFrame;

public class PantallaCompleta{
   private GraphicsDevice gd;//tarjeta gráfica
   public PantallaCompleta(){
      //hay que usar los medios del propio sistema
      GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
   gd = env.getDefaultScreenDevice();
 }
 
 public void setFullScreen(DisplayMode dm, JFrame ventana){
    ventana.setUndecorated(true);
    ventana.setResizable(false);
    gd.setFullScreenWindow(ventana);
    
    //comprobamos que el sistema soporta el cambio
    if(dm != null && gd.isDisplayChangeSupported()){
       try{
      gd.setDisplayMode(dm);
    }catch(IllegalArgumentException e){}
  }
 }
 
 public Window getFullScreenWindow(){
    return gd.getFullScreenWindow();
 }
 
 //restauramos los valores previos
 public void restoreScreen(){
    Window w = gd.getFullScreenWindow();
    if(w != null){
       w.dispose();
  }
  gd.setFullScreenWindow(null);
 }
}

TestPantallaCompleta.java

import java.awt.*;
import javax.swing.JFrame;

public class TestPantallaCompleta extends JFrame{
   public static void main(String[]args){
      //Pon aquí los valores que prefieras
      DisplayMode dm = new DisplayMode(1024,768,32,DisplayMode.REFRESH_RATE_UNKNOWN);
   TestPantallaCompleta test = new TestPantallaCompleta();
   test.run(dm);
 }
 
 public void run (DisplayMode dm){
    setBackground(Color.ORANGE);
    setForeground(Color.BLACK);
    setFont(new Font("Dialog", Font.PLAIN,48));
    PantallaCompleta pc = new PantallaCompleta();
    try{
       pc.setFullScreen(dm, this);
    try{
      //ponemos el tiempo que queremos que dure
      Thread.sleep(10000);
    }catch(InterruptedException ex){}
  }finally{//pase lo que pase, se restaura a sus antiguos valores
     pc.restoreScreen();
  }
 }
 
 public void paint(Graphics g){
    if (g instanceof Graphics2D){
       Graphics2D g2 = (Graphics2D) g;
    //difuminamos los bordes del texto
    g2.setRenderingHint(
       RenderingHints.KEY_TEXT_ANTIALIASING,
       RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    }     
           //el texto va a aparecer 300px a la derecha y 400px hacia abajo
    g.drawString("Aquí va a ir mi juego",300,400);
 }
}

Eso de "antialiasing" que ves al final del código, no es que no dejes a los jugadores tener un alias, sino que sirve para que el texto aparezca como escalonado, como escrito en cuadraditos.

A continuación te dejo una captura de pantalla para que veas cómo queda con los valores que yo le he asignado.



Si quieres más información sobre cómo crear juegos en Java, te recomiendo el libro Developing Games in Java, de David Brackeen, Bret Barker y Lawrence Vanhelsuwe, editorial New Riders Publishing. El código que te muestro aquí es una versión del que aparece en este libro. No sé si hay traducción en castellano, pero como creo que es muy interesante es seguro que en otras entradas os muestre otros ejemplos sacados de él.

Además he encontrado unos tutoriales en vídeo en Youtube, que su autor no lo dice, pero yo juraría que están basados en este libro. Están en inglés, pero aunque no lo entiendas, puede que el código que muestra te sea útil. Podéis encontralos en esta página.

1 comentario:

  1. Buenas tardes Sonia,

    llevo un rato intentando contactar contigo por correo electrónico sobre este tutorial, pero chica, no hay manera.

    En fin, expondré aquí mis dudas a ver si te llegan, aunque veo que tienes el blog algo desactualizado espero que te llegue este comentario.

    Esto siguiendo el libro al que haces referencia, Developing Games in Java de David Brackeen, y he picado el código (así pienso que es la mejor manera de aprender) y ejecutado sin que me dé problemas de compilación. Ahora viene la cosa más extraña por más vueltas que le doy no consigo dar con la tecla.

    Si ejecuto el código en linux todo ocurre con normalidad, las letras salen en el color y el tamaño que pongo y el fondo de la imagen en el color que especifico.
    Si ejecuto el código en el ordenador de casa con Windows 7 ocurre lo mismo, todo con normalidad.
    Sin embargo, si ejecuto exactamente el mismo en el ordenador del trabajo con Windows XP, el texto aparece, con el color deseado, sin embargo el fondo, da igual el color que determine con setBackground(Color.xxxxx); siempre sale negro.

    Después hay otra cosa que me vuelve loco y es el "this" en:

    pc.setFullScreen(dm, this);

    Hasta donde yo sé:

    setFullScreen() es un método definido en la clase Pantalla completa{}, se le pasa como parámetro un objeto Displaymode y un objeto JFrame, es decir, los datos de resolución, rango de colores, framerate y la ventana. Comprueba que los datos pasados en Displaymode son representables y si son correctos coge el objeto JFrame le quita el panel de menu y los bordes, le quita la posibilidad de cambiarle el tamaño y la maximiza a la resolución dada por el Dysplaymode. Hasta aquí correcto.

    El problema viene cuando se llama a este método desde la clase TestPantallaCompleta{}.

    En el método run() de TestPantallaCompleta{} crea un objeto PantallaCompleta llamado pc, es este objeto, pc, el que llama al método setFullScreen, como vimos antes setFullScreen tiene como parámetros un objeto Displaymode que se define al principio de la clase TestPantallaCompleta{} y despues una ventana JFrame que es la que se sustituye por this.

    Está claro que this hace referencia al objeto test que como la clase TestPantallaCompleta{} extiende la clase JFrame test tiene todas la propiedas de un JFrame a parte de las especificadas en la misma clase TestPantallaCompleta{}.
    Lo que no me da el entendimiento es como sabe Java que "this" hace referencia al objeto "test" y no al objeto "pc" que es el que está llamando al método...

    Sinceramente me estoy volviendo loco y ya me duele la cabeza de estrujarme la sesera.

    En fin chica, si hicieras el favor de despejarme estas dos dudas te aseguro que haraz a una persona inmensamente feliz =).

    Muchas gracias y un recibe un cordial saludo.

    Miguel

    ResponderEliminar