El Blog de Murphy

27 Noviembre 2008

3. A Full-Screen Exclusive Mode (FSEM) Worm – part 2

Archivado en: Killer Game Programming in Java — Etiquetas:, , , , , , , — Julio Cesar Cachay Pérez @ 1:17 pm

Como ustedes se habran fijado, me ha encantado lo que es manejo de imagenes en java 2d, y es qu esta preciosa la cosa, sobre todo cuando los programadores de sun te dejan muchas formas de hacerlo y tu solo tiens que elegir una, aun asi es bueo concocer como funcionana estads tecnicas en vista de escoger la mejor para cada uno.

Prosiguiendo con el capitulo

3.4. Information on Graphics Capabilities

Ahora se preguntaran, y que si mi tarjeta de video no soporta page flipping ¿como examinamos las capacidades de mi tarjeta de video? pues resulta que ademas de venir muy completo y listo para funcionar el codigo de Andrew hay que agradecer que el libro esta completisimo y ademas viene con un funcion que ilustra algunas de la formas de examinar las capacidades de la tarjeta grafica.

  private void reportCapabilities()
  {
    GraphicsConfiguration gc = gd.getDefaultConfiguration();

    // Capacidad de las imagenes
    ImageCapabilities imageCaps = gc.getImageCapabilities();
    System.out.println("Image Caps. isAccelerated: " + imageCaps.isAccelerated() );
    System.out.println("Image Caps. isTrueVolatile: " + imageCaps.isTrueVolatile());

    // Capacidad del buffer
    BufferCapabilities bufferCaps = gc.getBufferCapabilities();
    System.out.println("Buffer Caps. isPageFlipping: " + bufferCaps.isPageFlipping());
    System.out.println("Buffer Caps. Flip Contents: " +
                            getFlipText(bufferCaps.getFlipContents()));
    System.out.println("Buffer Caps. Full-screen Required: " +
                            bufferCaps.isFullScreenRequired());
    System.out.println("Buffer Caps. MultiBuffers: " + bufferCaps.isMultiBufferAvailable());
  } // fin de reportCapabilities()

  private String getFlipText(BufferCapabilities.FlipContents flip)
  {
    if (flip == null)
      return "false";
    else if (flip == BufferCapabilities.FlipContents.UNDEFINED)
      return "Undefined";
    else if (flip == BufferCapabilities.FlipContents.BACKGROUND)
      return "Background";
    else if (flip == BufferCapabilities.FlipContents.PRIOR)
      return "Prior";
    else // if (flip == BufferCapabilities.FlipContents.COPIED)
      return "Copied";
  } // fin de getFlipTest()

reportCapabilities() cisualiza las caracteristicas de las tarjetas graficas, en general, pueden haber muchos objetos GraphicsConfiguration asociados a una sola tarjeta grafica, cada uno representa diferentes modos de dibujo y/o capacidades

El obejto ImageCapabilities permite ver en detalle las caracteristicas de la aceleracion por hardware , isAccelerated() muestra su la aceleracion por hardaware es posible o no. En windows siempre devolvera verdadero la funcion isVolatile() (la imagen se peude perder en la VRam) si isAccelerated() devuelve verdadera

En general trodas las funciones con is… devuelven si la maquina es capaz o no de hacerlo.

getFlipContents() devuelve la tecnica usada por page Flipping:

FlipContents.COPIED -> esta valor significa que los contenidos son copiados a la pantalla

FlipContents.PRIOR -> Inidica que utilizamos el apuntador a la VRam, aunque no es garantizado

3.5. Rendering the Game

El codigo de run() seria:

        while(running) {
            gameUpdate();
            screenUpdate();

Como Veran se borran 2 funciones para dar paso a screenUpdate().

  private void screenUpdate()
  // usar renderizado activo
  { try {
      gScr = bufferStrategy.getDrawGraphics();
      gameRender(gScr);
      gScr.dispose();
      if (!bufferStrategy.contentsLost())
        bufferStrategy.show();
      else
        System.out.println("Contents Lost");
      // Sincroniza la pantalla en algunos sistemas
      // En linux arregla algunos problemas
      Toolkit.getDefaultToolkit().sync();
    }
    catch (Exception e)
    { e.printStackTrace();
      running = false;
    }
  }  // fin de screenUpdate()

gameRender()

  private void gameRender(Graphics gScr)
  {
    // limpia la pantalla
    gScr.setColor(Color.white);
    gScr.fillRect (0, 0, pWidth, pHeight);

	gScr.setColor(Color.blue);
    gScr.setFont(font);

    //Escribe el numero de frames y el promedio de FPS y UPS

	gScr.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", " +
                                df.format(averageUPS), 20, 25);  // was (10,55)

    // dibujka el timepo y las cajas usadas
    gScr.drawString("Time Spent: " + timeSpentInGame + " secs", 10, pHeight-15);
    gScr.drawString("Boxes used: " + boxesUsed, 260, pHeight-15);

    // dibuja la pausa y los botones de salida
    drawButtons(gScr);

	gScr.setColor(Color.black);

    // dibuja los elemntos dle juego (gusanos y cajas)
    obs.draw(gScr);
    fred.draw(gScr);

    if (gameOver)
      gameOverMessage(gScr);
  }  // fin de gameRender()

La diferencia fundamental es que ya no se necesita crear un dibujo fuera de pantalla. eso pasa a ser de initFullScreen añ momento de llamar a createBufferStrategy()

3.6. Finishing Off

El metodo finishOff() es llamado de la misma forma que en UFS().

 private void finishOff()
  /* Tareas que se dene hacer nates de terminar, llamado al final de run()
   * y via readyForTermination() al momentod e cerrar
   * no es necesarion en run() pero esta por seguridad
   */
 {
    if (!finishedOff) {
      finishedOff = true;
      printStats();
      restoreScreen();
      System.exit(0);
    }
  } // fin de finishedOff()
}

3.7. Displaying the Display Mode

initFullScreen() tien un metodo comenatdo setDisplayMode(), este metodo se usa solo despues de que la aplicacion haya entrado en el modo pantalla completa

la funcion showCurrentMode() devuleve le modo actual de la pantalla

  private void showCurrentMode()
  // imprime los detalles de la resolcuion de pantalla
  {
    DisplayMode dm = gd.getDisplayMode();
    System.out.println("Current Display Mode: (" +
                           dm.getWidth() + "," + dm.getHeight() + "," +
                           dm.getBitDepth() + "," + dm.getRefreshRate() + ")  " );
  }

3.8. Changing the Display Mode

¿Por qué cambiar el modo? pues porque necesitamos  incrementar la performance, entre menos resolucion de pantalla, menos peso en la data que se transfiere del buffer a la pantlla. aunque esta ventaja es irrelevante si pageflipping es el metod que se usa.

setDisplayMode() es le metodo que hace el cambio.

  private void setDisplayMode(int width, int height, int bitDepth)
  //
  {
    if (!gd.isDisplayChangeSupported()) {
      System.out.println("Display mode changing not supported");
      return;
    }

    if (!isDisplayModeAvailable(width, height, bitDepth)) {
      System.out.println("Display mode (" + width + "," +
                              height + "," + bitDepth + ") not available");
      return;
    }

    DisplayMode dm = new DisplayMode(width, height, bitDepth,
                              DisplayMode.REFRESH_RATE_UNKNOWN);
    try {
      gd.setDisplayMode(dm);
      System.out.println("Display mode set to: (" + width + "," +
                                         height + "," + bitDepth + ")");
    }
    catch (IllegalArgumentException e)
    {  System.out.println("Error setting Display mode (" + width + "," +
                                              height + "," + bitDepth + ")");  }

    try {  // dormimos para darle chance a la pantalla poder cambiar
      Thread.sleep(1000);  // 1 seg
    }
    catch(InterruptedException ex){}
  }  // fin de setDisplayMode()

Ademas se ve si se puede hacer el cmabio de resolucion

  private boolean isDisplayModeAvailable(int width, int height, int bitDepth)
  /* cheka si se peude hacer el cmabio de resolucion
  */
  { DisplayMode[] modes = gd.getDisplayModes();
    showModes(modes);

    for(int i = 0; i < modes.length; i++) {
      if (width == modes[i].getWidth() && height == modes[i].getHeight() &&
          bitDepth == modes[i].getBitDepth())
        return true;
    }
    return false;
  }  // fin de isDisplayModeAvailable()

showModes() imprime todos los modos disponibles

   private void showModes(DisplayMode[] modes)

  {
    System.out.println("Modes");
    for(int i = 0; i < modes.length; i++) {
      System.out.print("(" + modes[i].getWidth() + "," +
                               modes[i].getHeight() + "," +
                               modes[i].getBitDepth() + "," +
                               modes[i].getRefreshRate() + ")  " );
      if ((i+1)%4 == 0)
        System.out.println();
    }
    System.out.println();
  }   // fin de showModes()

20 Noviembre 2008

3. A Full-Screen Exclusive Mode (FSEM) Worm – part 1

Archivado en: Killer Game Programming in Java — Etiquetas:, , , , , , , — Julio Cesar Cachay Pérez @ 6:16 am

Esta parte es por demas interesante, El modo exclusivo de pantalla completa lo que hace es suspende el entrono windows de java (llamese las capas swing o awt) ofreciendo un acceso casi directo a la pantalla, dandonos oportunidad de controlar algunas carateristicas de las tarjetas gráficas, como son page flipping y stereo buffering, y tb controlar la resolucion de pantalla

Existe una pequeña desventaja, la memoria de video es muy volatil por lo que el SO (Sist. Ope.) puede redibujar la pantalla cuando, por ejemplo, necesita dibujar una ventana, o levanta el protector de pantalla. Entonces la imagen tendra que ser reconstruida desde 0, tampoco es un recurso inifinito, tratar de colocarle muchas imagenes hará que el SO los comience a intercambiar fuera de la memoria haciendo lento el renderizado.

Por eso vemos lo que es VolatileImage, que permite a la imagenes tomar ventaja de la VRAM (memoria de video). Les recomiendo leer lo que es VolatileImage

Bueno si ya leyeron lo del volatileImage la verdad es que el practica no es requerida usualmente ia que la mayoria de aplicaciones, en swing, emplean la aceleracion por hardware de por si, en vez d eso utilizan VolatileImage como double buffer.

Otra cosa que debrian sabes es que los dibujos de lineas diagonales o curvas utilizan aceleracion por software y utilizar VolatileImage aca seria contraproducente, lean la documentacion ^_^

Otra cosa VolatileImage es una caracteristica mas recomendada en windows ya que este es un SO que permite la manipulacion directa de la vram, en solaris o linux se usa las pixmap

Bueno si dibujaramos el uml tendria que desparecer los windows listener y el jpanel y muchas funciones que venia con ellas en las clases, por lo tanto se reducira en un porcentaje el codigo que utilizamos asi quedarian, ademas le decimos adios a WormPanel para que WormChase ocupe su lugar.

3.1. The WormChase Class

Ahora WormChase sera el nuevo WormPanel

agregamos estas variables globales

  private static final int NUM_BUFFERS = 2;    // usado para page flipping
  private static int DEFAULT_FPS = 100;

    // usado por el modo exclusivo de pantalla completa
  private GraphicsDevice gd;
  private Graphics gScr;
  private BufferStrategy bufferStrategy;

Modificamos el constructor

  public WormChase(long period)
  {
    super("Worm Chase");
    this.period = period;
    initFullScreen();// cambia a FSEM

    readyForTermination();

    // crea componentes del juego
    obs = new Obstacles(this);
    fred = new Worm(pWidth, pHeight, obs);
    gameOver = false;

    addMouseListener( new MouseAdapter() {
      public void mousePressed(MouseEvent e)
      { testPress(e.getX(), e.getY()); }
    });

    addMouseMotionListener( new MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e)
      { testMove(e.getX(), e.getY()); }
    });
    // fuente del mensaje
    font = new Font("SansSerif", Font.BOLD, 24);
    metrics = this.getFontMetrics(font);

    // especifica las areas en pantalla para los botones
    pauseArea = new Rectangle(pWidth-100, pHeight-45, 70, 15);
    quitArea = new Rectangle(pWidth-100, pHeight-20, 70, 15);    

    fpsStore = new double[NUM_FPS];
    upsStore = new double[NUM_FPS];
    for (int i=0; i < NUM_FPS; i++) {
      fpsStore[i] = 0.0;
      upsStore[i] = 0.0;
    }
    gameStart();
  }  // fin de WormPanel()

Ahora gameStart() es lo que fue addNotify()

    private void gameStart(){

        if (animator == null || !running) {  // comienza el hilo
          animator = new Thread(this);
              animator.start();
        }
    }

3.2. Setting up Full-Screen Exclusive Mode

Ahora comenzamos a declarar las funcion initFullScreen()

      private void initFullScreen()
      {
            GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            gd = ge.getDefaultScreenDevice();

            setUndecorated(true);    // Sin la barra de menu, bordes, etc. o cualuqier componente Swing
            setIgnoreRepaint(true);  // apaga todos los eventos de pintado ya que estamos utilizando renderizado activo
            setResizable(false);

            if (!gd.isFullScreenSupported()) {
              System.out.println("Full-screen exclusive mode not supported");
              System.exit(0);
            }
            gd.setFullScreenWindow(this); // cambian al modo exclusivo de pantalla completa

            //Podemos ajustar el modo en q se muestra si asi lo deseamos
            showCurrentMode();

            // setDisplayMode(800, 600, 8);
            // setDisplayMode(1280, 1024, 32);

            pWidth = getBounds().width;
            pHeight = getBounds().height;

            setBufferStrategy();
      }  // fin de initFullScreen()

La tarjeta grafica es accesible a raves del objeto GraphicsDevice. utilizamos la funcion gd.isFullScreenSupported() que nos devuelve si se peude usar el modo de pantalla completa o no, si no se pudiera usar el modo a pantalla completa deberia poder usarse el modo AFS o UFS, pero aca solo salimos del programa.

Despues utilizamos la funcion showCurrentMode() para ver las caracteristicas actuales de la pantalla, y la funcion que puede cambiar las resolucion de la pantalla y los bits, es setDisplayMode() que esta comentada.

  private void showCurrentMode()
  // imprime los detalles de la resolcuion de pantalla
  {
    DisplayMode dm = gd.getDisplayMode();
    System.out.println("Current Display Mode: (" +
                           dm.getWidth() + "," + dm.getHeight() + "," +
                           dm.getBitDepth() + "," + dm.getRefreshRate() + ")  " );
  }

Ahora veremos la fucnion setBufferStrategy() que almacena la estrategias de buffer para la actualizacion de la pantalla

    private void setBufferStrategy()
  /* Enciende el page flipping: NUM_BUFFERS == 2 por lo tanto habra
   * una "superficie primaria" y su "respaldo"
   *
   * createBufferStrategy() es una operacion asincrona, por lo
   * tanto debemos dormir un ratito para que la funcion getBufferStrategy()
   * obtenga los detalles correctos
   */
  { try {
      EventQueue.invokeAndWait( new Runnable() {
        public void run()
        { createBufferStrategy(NUM_BUFFERS);  }
      });
    }
    catch (Exception e) {
      System.out.println("Error cuando se crea la estrategia buffer");
      System.exit(0);
    }

    try {  // duerme para darle tiempo a la estrategia que se lleve a cabo
      Thread.sleep(500);  // 0.5 seg
    }
    catch(InterruptedException ex){}

    bufferStrategy = getBufferStrategy();  // guarda para despues
  }  // fin de setBufferStrategy()

La naturaleza asincrona de muchos de los metodos de FSEM es la debilidad de ete API ya que es muy dificl sabre cuando una operacion ha sido completada, utilizar sleep() de forma arbitraria es una forma que no debheria darse y que quizas estemos bajando la velocidad de ejecucion sin ninguna necesidad. Otros metodos asincronos son setDisplayMode(), show(), y setFullScreenWindow().

Algo que quiero explicar antes de continuar es algo que estuve revisando ahora, lo que pasa es que este libro fue escrito cuando el jdk 1.4 estaba en el aire, pero ¿hello? ia estamos en jdk 1.6 muchas cosas se han mejorado, un blog que es muy interesante de leer, tiene un tema que discute sobre que tipo de imagenes o estrategias a usar, se llama ToolkitBufferedVolatileManagedImage Strategies y esta interesantisimo sobre todo cuando habla Managed Images, si tengo tiempo en post mas adelante lo voy a explicar ya que en esto de juegos, sobre todo en el manejo de Sprites es muy importante.

Tambien es importante que lean acerca de las mejora que se hacen en java 2D con cada nueva version del jdk, por ejemplo el 1.5 o el 1.6

3.3. Double Buffering, Page Flipping, and More

Lo que antes hicimos era dibujar una imagen en un buffer fuera de pantalla y luego copiarlo a la pantalla con drawImage, esto en terminos de resolucion de pantalla y bits se traduciria, por ejemplo en 1024 x 768 y 32 bits, en 3mb por frame, y si hablamos de 80 FPS imaginense.

Page flipping evade todo esto al utilizar un “video pointer” (si se puede), un video pointer le dice a tu tarjeta de video adonde buscar la imagen a mostrar en su VRAM. Para esto Page Flipping utiliza 2 buffers, mientras 1 se muestra en pantalla, el otro se actualiza y llegado el momento de refrescar la pantalla se intercambian.

La gran ventaja de esta tecnica es que solo mueves el video pointer sin necesidad de copiar nada.

Ahora ¿por que no utilizar mas de 1 buffer? bueno eso es el siguiente paso, usar 2 o mas buffers como una cadena, esto se llama “flip chain”. ¿Que sucede? que ahora el video pointer hace ciclos a traves de los buffers mientras que los otros son renderizados.

Y ¿como hacemos esto? pues utilizaremos una tecnica que me gusta llamar “Los programadores de Sun lo han hecho por ti”, es bastante sencilla y util, y no solo eso, sino que ya la hemos llevado a cabo, lo hicimos en la funcion setBufferStrategy(), especificamente en la linea: createBufferStrategy(NUM_BUFFERS); al cual le hemos dicho que use 2 buffers.

Bueno para los que quieran saber que es lo que sucede en esta linea de codigo, les voy a explicar muy brevemente, primero se tratara de utilizar la estrategia de Page Flipping, y para esto utilizamos la aceleracion por hardware, si en caso no se pudiera se utlizaria una estrategia de copiado sin aceleramiento.

Es momento de sacar el diccionario y buscar que es “aceleracion por hardware”, lindo io, les voya dar un pequeño empujoncito. Existen muchas tecnicas para mejorar el rendimiento de un cpu, una de ellas es la mencionada, y lo que pasa es que la aceleracion por software es concurrente, osea una a una las tareas van pasando mientras que en hardware podemos tener muchas al mismo tiempo, esto requiere de codigos mas pesados, y de procesadores diferentes al del cpu, pues les comento, que si no saben o no han escuchado vayan a leer un poquito porque es cultura general, que las tarjetas graficas (no todas) tienen GPU’s osea unidades de procesado de graficos, mas al español procesadores exclusivos para graficos, y como ia deben haberse figurado es aqui donde trabaja createBufferStrategy().

Tarea: Si estan en Windows vayan a las propiedades pantalla-configuracion, luego click en el boton opciones avanzadas y despues en la pestaña solucionador de problemas y ¡Wala! veran que uds pueden desactivar la aceleracion por hardware, interesante ¿no?

Y Dios que se me esta haciendo larguisimo este post asi que mejor lo divido en 2 ahora, o sino llego al mes y sigo sin postear

7 Noviembre 2008

2. An Undecorated Full-Screen (UFS) Worm

Archivado en: Killer Game Programming in Java — Etiquetas:, , , , , , , — Julio Cesar Cachay Pérez @ 4:11 am

Ahora trabajeremos sobre una pantalla que no tiene barra de titulo ni cajas de texto, entonces para poder salir haremos botones de pausa y salida del juego.

La clase worm Chase ya no necesitara de WindowListener es por eso que las funciones pauseGame(), resumeGame(), o stopGame() ya no seran requeridos.

2.1. The WormChase Class

Se reduce bastante el codigo que se usa en esta clase.

  public WormChase(long period)
  { super("The Worm Chase");

    Container c = getContentPane();
    c.setLayout( new BorderLayout() );   

    WormPanel wp = new WormPanel(this, period);
    c.add(wp, "Center");

    setUndecorated(true);   // no hay bordes ni barra de titulo
    setIgnoreRepaint(true);  //  apaga todos los eventis de pintado ya que
    //habra renderizado activamente
    pack();
    setResizable(false);
    setVisible(true);
  }  // fin de WormChase()

  // -----------------------------------------

  public static void main(String args[])
  {
    int fps = DEFAULT_FPS;
    if (args.length != 0)
      fps = Integer.parseInt(args[0]);

    long period = (long) 1000.0/fps;
    System.out.println("fps: " + fps + "; period: " + period + " ms");

    new WormChase(period*1000000L);    // ms --> nanosecs
  }  

} // fin de la clase WormChase

setIgnoreRepaint() es utilizado ya que no existe ningun componente GUI, y no se becesitaran eventos de pintado

2.2. The WormPanel Class

El constructor de WormPanel pone el tamaño de la pantalla y guarda las dimensiones en las globales pWidth y pHeight

    Toolkit tk = Toolkit.getDefaultToolkit();
    Dimension scrDim = tk.getScreenSize();
    setPreferredSize(scrDim);// pone el tamaño al panel
    pWidth = scrDim.width;// guarda las dimensiones para despues
    pHeight = scrDim.height;

El constructor crea 2 rectangulos, llamados pauseArea y quitArea, el dibujo de estos se describe abajo:

    private Rectangle pauseArea, quitArea; // globales
    // en WormPanel()
    // especifica las areas en pantalla para los botones
    pauseArea = new Rectangle(pWidth-100, pHeight-45, 70, 15);
    quitArea = new Rectangle(pWidth-100, pHeight-20, 70, 15);

2.3. Button Behaviour

Para darle una mejoria a los botones, le vamos a dar efectos, por ejemplo cuando pase sobre los botones se pondra verde y cuando el juego este pausado el boton se va a llamar “paused” para esto:

addMouseMotionListener( new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e)
{ testMove(e.getX(), e.getY()); }
});
-
-
-
  private void testMove(int x, int y)
  // esta (x,y) encima de los obtones de pausa o salida?
  {
    if (running) {   // para los problemas con un movimiento rapido despues
        //de presionar "quit"
      isOverPauseButton = pauseArea.contains(x,y) ? true : false;
      isOverQuitButton = quitArea.contains(x,y) ? true : false;
    }
  }

Ahora tenemos que definir como lidiar con el click del mouse si choca con un boton, extendermos testPress para esto:

// en el constructor WormPanel
addMouseListener( new MouseAdapter() {
public void mousePressed(MouseEvent e)
{ testPress(e.getX(), e.getY()); }
});
:
private void testPress(int x, int y)
{
if (isOverPauseButton)
isPaused = !isPaused;
else if (isOverQuitButton)
running = false;
else {
if (!isPaused && !gameOver) {
//presionaron el mouse erca de la cabeza?
. . .
}
}
}

2.4. Drawing the Game Canvas

Hay nuevos elementos, la info del tiempo y las cajas ahora sera dibujada en la parte de abjo, y los botones de pausa y salida. todo esto se implementa en gamerender()

  private void gameRender()
    {
        if (dbImage == null){
          dbImage = createImage(pWidth, pHeight);
          if (dbImage == null) {
            System.out.println("dbImage is null");
            return;
          }
          else
            dbg = dbImage.getGraphics();
        }

        // limpia el background
        dbg.setColor(Color.white);
        dbg.fillRect(0, 0, pWidth, pHeight);

        dbg.setColor(Color.blue);
        dbg.setFont(font);

        //En la esquina izquierda se hara un conteo y promedio de FPS y UPS
        dbg.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", " +
        df.format(averageUPS), 20, 25);  // was (10,55)

        // reporta el tiempo y cajas usadas  en la esquina de abajo a la izquierda
        dbg.drawString("Time Spent: " + timeSpentInGame + " secs", 10, pHeight-15);
        dbg.drawString("Boxes used: " + boxesUsed, 260, pHeight-15);

        // Dibuja los botones de pausa y salida
        drawButtons(dbg);

        dbg.setColor(Color.black);

        // dibuja los elementos del juego
        obs.draw(dbg);
        fred.draw(dbg);

        if (gameOver)
            gameOverMessage(dbg);
    }  // fin de gameRender()
-
-
-
  private void drawButtons(Graphics g)
  {
    g.setColor(Color.black);

    // dibujar el boton de pausa
    if (isOverPauseButton)
      g.setColor(Color.green);

    g.drawOval( pauseArea.x, pauseArea.y, pauseArea.width, pauseArea.height);
    if (isPaused)
      g.drawString("Paused", pauseArea.x, pauseArea.y+10);
    else
      g.drawString("Pause", pauseArea.x+5, pauseArea.y+10);

    if (isOverPauseButton)
      g.setColor(Color.black);

    // dibujar el boton de salida
    if (isOverQuitButton)
      g.setColor(Color.green);

    g.drawOval(quitArea.x, quitArea.y, quitArea.width, quitArea.height);
    g.drawString("Quit", quitArea.x+15, quitArea.y+10);

    if (isOverQuitButton)
      g.setColor(Color.black);
  }  // dibujar los botones

2.5. Exiting the Game

se crea un nuevo metodo:

private void finishOff()
{ if (!finishedOff) {
finishedOff = true;
printStats();
System.exit(0);
}
}

Hay un codigo que nos permite hacer los reportes de las estadisticas aun si el programa fue terminado inesperaamente, esta funcion es critica por ejemplo si necesitas que se guarde algo antes de que salga del programa.

Runtime.getRuntime().addShutdownHook(new Thread() {
public void run()
{ running = false;
System.out.println("Shutdown hook executed");
finishOff();
}
});

2.6. Timings for UFS

Las estadisticas de UFS

1. An Almost Full-Screen (AFS) Worm

Archivado en: Killer Game Programming in Java — Etiquetas:, , , , , , , , — Julio Cesar Cachay Pérez @ 12:11 am

Una aplciacion casi full screen es una aplicacion que esta maximizada en pantalla, el cambio en el codigo es muy pequeño, asi tenemos:

1.1. The WormChase Class

Ahora le constructor debera calcular, no solo la nueva dimension, sino tambien la barra de tareas de windows y otros componentes swing en la ventana (los 2 textfields)

Modificaremos el constructor:

private int pWidth, pHeight;   // dimensiones de el panel
public WormChase(long period)
  { super("The Worm Chase");

    makeGUI();

    pack();   // primero (El GUI o incluye ek JPanel todavia)
    setResizable(false);   // el tamaño podria variar si ponemos true
    calcSizes();
    setResizable(true);

    Container c = getContentPane();
    wp = new WormPanel(this, period, pWidth, pHeight);
    c.add(wp, "Center");
    pack();  // segundo, despues de agregar a JPanel

    addWindowListener( this );

    addComponentListener( new ComponentAdapter() {
      public void componentMoved(ComponentEvent e)
      /* Llamado por el ComponentListener cuando el jFrame es movido
       * . Lo pone todo en su sitio de nuevo*/
      {  setLocation(0,0);  }
    });

    setResizable(false);
    setVisible(true);
  }  // fin de WormChase()

makegui() hace la GUI sin dibujarala:

  private void makeGUI()
  // crea la GUI, Menos el area de dibujo (Jpanel)
  {
    Container c = getContentPane();    // BorderLayout usado por default

    JPanel ctrls = new JPanel();   // una fila de textfields
    ctrls.setLayout( new BoxLayout(ctrls, BoxLayout.X_AXIS));

    jtfBox = new JTextField("Boxes used: 0");
    jtfBox.setEditable(false);
    ctrls.add(jtfBox);

    jtfTime = new JTextField("Time Spent: 0 secs");
    jtfTime.setEditable(false);
    ctrls.add(jtfTime);

    c.add(ctrls, "South");
  }  // fin de makeGUI()


calcSizes() inicializa 2 variables locales pWidth y pHeight, que luego pasan a WormPanel

  private void calcSizes()
  /* Calcula el tamaño del panel de dibujo para llenar la pantalla,
   * deja espacio para los otros componentes de Jframe y la barra
   * de tareas del sistema operativo*/
  {
    GraphicsConfiguration gc = getGraphicsConfiguration();
	Rectangle screenRect = gc.getBounds();
    // System.out.println("Screen size: " + screenRect);

    Toolkit tk = Toolkit.getDefaultToolkit();
    Insets desktopInsets = tk.getScreenInsets(gc);
    // System.out.println("OS Insets: " + desktopInsets);

    Insets frameInsets = getInsets();     // funciona despues de llamar a pack()
    // System.out.println("JFrame Insets: " + frameInsets);

    Dimension tfDim = jtfBox.getPreferredSize();   // tamaño del textfield
    // System.out.println("Box TF Size: " + tfDim );
    // System.out.println("Time TF Size: " + jtfTime.getPreferredSize() );  

    pWidth = screenRect.width - (desktopInsets.left + desktopInsets.right)
                              - (frameInsets.left + frameInsets.right);

    pHeight = screenRect.height - (desktopInsets.top + desktopInsets.bottom)
                                - (frameInsets.top + frameInsets.bottom)
                                - tfDim.height;

    // System.out.println("pWidth: " + pWidth + "; pHeight: " + pHeight);
  }  // fin de calcSizes()

1.2. Stopping Window Movement

El codigo para mover la ventana al centro siemrpe que se quiera mover es

    addComponentListener( new ComponentAdapter() {
      public void componentMoved(ComponentEvent e)
      /* Llamado por el ComponentListener cuando el jFrame es movido
       * . Lo pone todo en su sitio de nuevo*/
      {  setLocation(0,0);  }
    });

1.3. Timings for AFS

Calculos estadisticos para AFS, no los tomare en cuenta

1.4. Cambios en WormPanel

  private long period;                // periodo entre dibujos, en ms
  private int pWidth, pHeight;   // dimensiones de el panel
  -
  -
   public WormPanel(WormChase wc, long period, int w, int h)
  {
    wcTop = wc;
    this.period = period;
    pWidth = w; pHeight = h;

    setBackground(Color.white);
    setPreferredSize( new Dimension(pWidth, pHeight));

    setFocusable(true);
    requestFocus();    // El JPanel ahora es enfocado
	readyForTermination();

    // crea componentes del juego
    obs = new Obstacles(wcTop);
    fred = new Worm(pWidth, pHeight, obs);
    gameOver = false;

    addMouseListener( new MouseAdapter() {
      public void mousePressed(MouseEvent e)
      { testPress(e.getX(), e.getY()); }
    });

    // fuente del mensaje
    font = new Font("SansSerif", Font.BOLD, 24);
    metrics = this.getFontMetrics(font);

    fpsStore = new double[NUM_FPS];
    upsStore = new double[NUM_FPS];
    for (int i=0; i < NUM_FPS; i++) {
      fpsStore[i] = 0.0;
      upsStore[i] = 0.0;
    }
  }  // fin de WormPanel()

6 Noviembre 2008

Chapter 3. Full-Screen Worms

Archivado en: Killer Game Programming in Java — Etiquetas:, , , , , , , — Julio Cesar Cachay Pérez @ 8:27 pm

Estamos llegando a la parte de las aplicaciones de pantalla completa, este capitulo tratare de pasarlo rapido, porque los capitulos ma sinteresantes estan por venir

Una forma de hacer que un jugador se “pegue” al juego, olvidandose de su mundo alrededor, es haciendo aplicaciones de pantalla completa, en este capitulo se tocaran 3 temas:

  1. un casi full-screen JFrame (AFS);
  2. un no-decorado full-screen JFrame (UFS);
  3. un full-screen en modo exlusivo (FSEM)
Entradas más antiguas »

Blog de WordPress.com.