viernes, 26 de diciembre de 2014

Programación de juegos para móviles con J2ME Parte V

La interfaz gráfica de bajo nivel

Cuando se diseñó J2ME, los ingenieros de Sun ya sabían que una de las claves para que su tecnología tuviera éxito era que tenía que ser capaz de hacer funcionar juegos, y hacerlo de forma medianamente decente. Para ello debían dotar a los MIDlets de la capacidad de controlar la pantalla al más bajo nivel posible, es decir, a nivel gráfico.
En el capítulo anterior, hemos profundizado en las clases que nos permitían trabajar con interfaces de usuario. Todas ellas derivaban de la clase Screen, que a su vez derivaba de Displayable. Si nos fijamos en el diagrama de clases vemos como de Displayable también deriva la clase Canvas. Esta clase es capaz de mostrar información gráfica a nivel de píxel. Es por ellos que la llamamos interfaz de bajo nivel.

Básicamente podemos realizar tres operaciones sobre un Canvas:
  • Dibujar primitivas gráficas
  • Escribir texto
  • Dibujar imágenes
Es este capítulo vamos a cubrir estas operaciones, y así sentar las bases necesarias para abordar las materias concretas relativas a la programación de videojuegos.
Tal y como hicimos en el anterior capítulo utilizaremos un código de ejemplo para ir explicando sobre él los conceptos básicos.

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class Canvas1 extends MIDlet implements CommandListener {

 private Command exitCommand;
 private Display display;
 private SSCanvas screen;

 public Canvas1() {
 display=Display.getDisplay(this);
 exitCommand = new Command("Salir",Command.SCREEN,2);

 screen=new SSCanvas();
 screen.addCommand(exitCommand);
 screen.setCommandListener(this);
 }
 
 public void startApp() throws MIDletStateChangeException {
 display.setCurrent(screen);
 }

 public void pauseApp() {}

 public void destroyApp(boolean unconditional) {}

 public void commandAction(Command c, Displayable s) {

 if (c == exitCommand) {
  destroyApp(false);
  notifyDestroyed();
 }

 }
 
}

class SSCanvas extends Canvas {

 public void paint(Graphics g) {

 g.setColor(255,255,255);
 g.fillRect (0, 0, getWidth(), getHeight());

 q.setColor(10,200,100);
   g.drawLine (0, 0, 100, 100);
   g.fillRect (50, 50, 30, 30); 
 }
}
La Primera parte del código, es decir, la clase Canvas1 debe ya sernos familiar. La única diferencia con el código del capítulo anterior, es que, en lugar de utilizar un objeto de la clase Form como pantalla principal de la aplicación, utilizamos uno derivado de la clase SSCanvas que implementamos justo debajo.
private SSCanvas screen;
Como puedes observar, la clase SSCanvas hereda de la clase Canvas. Es por ello que podemos utilizarla como pantalla principal (recuerda que Canvas es una clase derivada de Displayable).
class SSCanvas extends Canvas {
La clase SSCanvas implementa el método paint(), que hereda de la clase Canvas. Éste es el método que se invoca cada vez que la pantalla necesita ser redibujada. Por lo tanto, todo el código encargado de pintar en la pantalla ha de situarse aquí.
public void paint(Graphics g) {
El parámetro g, es el llamado contexto gráfico, que es de tipo Graphics. Esta clase posee los métodos necesarios para dibujar en pantalla, mostrar gráficos y mostrar textos. Las siguientes líneas se ocupan de borrar la pantalla y dibujar una línea y un rectángulo utilizando los métodos de la clase Graphics.
Vamos a entrar a continuación en detalles sobre los métodos de esta clase.

Primitivas Gráficas

Colores

En la naturaleza hay millones de posibles tonalidades de color. necesitamos pues un método que nos permita expresar un color concreto de una gama muy amplia. Newton, experimentando con un prisma de vidrio, constató como la luz podía descomponerse en una serie de colores básicos. Lo cierto es que este experimento sólo trataba de reproducir lo que en la naturaleza conocemos como arco iris, que se produce por la difracción de la luz al atravesar las gotas de agua. Un color, por lo tanto, se forma a partir de las distintas aportaciones de los colores básicos. Según predomine más o menos cada color básico, el color resultante será distinto. Utilizando esta misma técnica, en informática utilizamos tres colores básicos para especificar colores complejos. Estos colores son el Rojo, el Verde y el Azul, y abreviamos con RGB (de Red, Green y Blue).
Código RGB Color
255, 0, 0 Rojo
0, 255, 0 Verde
0, 0, 255 Azul
128, 0, 0 Rojo oscuro
255, 255, 0 Amarillo
0, 0, 0 Negro
255, 255, 255 Blanco
128, 128, 128 Gris
Para especificar el color que queremos utilizar al dibujar en la pantalla utilizamos el método setColor() de la clase Graphics.
void setColor(int rojo, int verde, int azul)
Los parámetros de color tienen un rango de 0 a 255.
Pero, no todos los dispositivos poseen pantalla a color. El siguiente método establece un tono de gris dentro de la grama de grises de una pantalla monocromo.
void setGrayScale(int tono)
El parámetro tono puede tomar un valor entre 0 y 255.

Primitivas

Aunque no vamos a necesitarlas muy a menudo para desarrollar nuestros juegos, es interesante que conozcamos las primitivas básicas con las que contamos para dibujar.
void drawLine (int x1, int y1, int x2, int y2)
Este método dibuja una línea que une los puntos de la coordenada (x1, y1) de la pantalla y la coordenada (x2, y2).
void drawRect (int x, int y, int ancho, int alto)
Con drawRect() podemos dibujar rectángulos. Los parámetros x e y indican cual será la esquina superior izquierda del rectángulo, mientras que los otros dos parámetros nos indican el ancho y el alto que tendrá el rectángulo en píxeles.
En nuestro programa de ejemplo, además de utilizarlo para dibujar un cuadrado, le hemos asignado la misión de borrar la pantalla. Esto se hace dibujando un rectángulo del tamaño de la pantalla del dispositivo y de color blanco.
void drawRoundRect (int x, int y, int ancho, int alto, int ancho_arco, int alto_arco) 
Este método es similar a drawRect(), excepto porque sus esquinas son redondeadas. Los otros dos parámetros son al ancho y el alto del arco de las esquinas.
void drawArc(int x, int y, int ancho, int alto, int ángulo_inicial, int ángulo)
Con drawArc() seremos capaces de dibujar secciones de arco. Los cuatro primeros parámetros no deberían necesitar ya explicación. El perámetro ángulo_inicial indica cual será el ángulo a partir del que se comenzará a dibujar el segmento de arco. El parámetro ángulo indica la longitud de arco (en grados) que será dibujado.
void fillRect (int x, int y, int ancho, int alto)

void fillRoundRect (int x, int y, int ancho, int alto, int ancho_arco, int alto_arco) 

void fillArc(int x, int y, int ancho, int alto, int ángulo_inicial, int ángulo)
Estos tres métodos son iguales que los anteriores con la diferencia de que la primitiva que dibujan estará rellena. Es decir, dibuja las primitivas como sólidas en lugar de huecas.

Texto

Aunque estemos trabajando a nivel gráfico, es muy probable que necesitemos mostrar información textual en pantalla. Un ejemplo claro es el marcador de puntuación de un juego.
El método que nos permite escribir texto en un Canvas es:
void drawString (String texto, int x, int y, int ancla)
El primer parámetro es el texto que queremos mostrar. Los parámetros x e y es la posición donde queremos situar el texto dentro de la pantalla. El cuarto parámetro indica cuál es el punto de referencia para situar el texto en las coordenadas deseadas. Los valores posibles son TOP, BASELINE y BUTTOM para la posición vertical del texto y LEFT, HCENTER y RIGHT para la posición horizontal del texto. Por ejemplo, si quisiéramos posicionar un texto en la posición 100,100 y con centro en la esquina superior izquierda utilizaremos la siguiente línea:
g.drawString (“Hola.”, 100, 100, Graphics.LEFT | Graphics.TOP);

Ahora que tenemos control sobre la posición del texto, nos falta por controlar el tipo de letra que queremos utilizar.
void setFont(Font fuente) 
Esta función selecciona la fuente a utilizar. El método getFont() de la clase Font nos devuelve un objeto de tipo Font que podemos utilizar con setFont().
static Font getFont (int espaciado, int estilo, int tamaño)
Los valores posibles para estos parámetros son:
Para... Variables
Tamaño SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE
Estilo STYLE_PLAIN, STYLE_ITALICS, STYLE_BOLD, STYLE_UNDERLINED
Espaciado FACE_SYSTEM, FACE_MONOSPACE, FACE_PROPORTIONAL
Veamos un ejemplo:
Font fuente = Font.getFont (Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM);

g.setFont(fuente);

Imágenes

Las primitivas gráficas nos permiten ya cierta capacidad de dibujar gráficos, pero para crear un videojuego, necesitamos algo más elaborado. La clase Graphics nos ofrece dos métodos:
public static Image createImage(String name) throws IOException
El método createImage() carga un archivo gráfico en formato .PNG. Dependiendo del dispositivo podrá soportar más formatos gráficos, pero en principio, al menos, debe soportar el formato .PNG. Recuerda que los gráficos (y el resto de recursos, como sonidos, etc...) han de estar en el directorio ‘res’.
boolean drawImage(Image img, int x, int y, int ancla)
El último parámetro es similar al de drawString(). Sus posibles valores son TOP, VCENTER y BUTTON para la posición vertical y LEFT, HCENTER, RIGHT para la posición horizontal.
Veamos un ejemplo:
Image img = Image.createImage(“\logo.png”);

g.drawImage (img, 10, 10, Graphics.HCENTER, Graphics.VCENTER);
El siguiente código es un ejemplo real de lo que hemos visto en éste capítulo.

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class Canvas2 extends MIDlet implements CommandListener {

 private Command exitCommand;
 private Display display;
 private SSCanvas screen;

 public Canvas2() {
 display=Display.getDisplay(this);
 exitCommand = new Command("Salir",Command.SCREEN,2);

 screen=new SSCanvas();
 screen.addCommand(exitCommand);
 screen.setCommandListener(this);
 }
 
 public void startApp() throws MIDletStateChangeException {
 display.setCurrent(screen);
 }

 public void pauseApp() {}

 public void destroyApp(boolean unconditional) {}

 public void commandAction(Command c, Displayable s) {

 if (c == exitCommand) {
  destroyApp(false);
  notifyDestroyed();
 }

 }
 
}

class SSCanvas extends Canvas {

 public void paint(Graphics g) {

  Image img=null;
  
 // Borrar la pantalla
 g.setColor(255,255,255);
 g.fillRect (0, 0, getWidth(), getHeight());

 // Dibujar línea
 g.setColor(10,200,100);
    g.drawLine (0, 80, getWidth(), 80);
     
    // Poner texto
 Font fuente = Font.getFont (Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
 g.setFont(fuente);
    
 g.drawString("J2ME", getWidth()/2, 10,Graphics.BASELINE|Graphics.HCENTER);
 
 // Cargar y mostrar gráfico
 try {
  img = Image.createImage("/logo.png");
 } catch (Exception e) {
  System.err.println("error: " + e);
 }

 g.drawImage (img, getWidth()/2, 40, Graphics.HCENTER|Graphics.VCENTER);
 
 }
}

No hay comentarios.:

Publicar un comentario