viernes, 16 de abril de 2010

Enemigos

Hoy hemos visto como programar los diferentes enemigos que van a salir por pantalla.
Hemos visto un poco de teoría sobre Inteligencia Artificial (un poco de culturilla, nada importante) y nos hemos puesto a la tarea.
Lo primero de todo es decidir el comportamiento de nuestro enemigo. Vamos a hacer un enemigo que se mueva en vertical por nuestra pantalla del móvil y que al llegar a una determinada altura (50), se mueva en diagonal.
De paso vemos alguna técnica propia de Java, como es la de crearnos una nueva clase a la que llamamos Enemigos (nos hacemos un nuevo archivo del tipo Clase en Netbeans) que va a extender de la clase Sprite.
El código de la nueva clase es el siguiente:
public Class Enemigos extends Sprite {
    private int Estado, intX, intY;
    public int getEstado(){
       return Estado;
    }
    public void setEstado(int nEstado){
       Estado=nEstado;
    }
    public void calcular_movimiento() {
       Random rdValor=new Random();
       if (getY()>50 && Estado!=2) {
            Estado=2;
            if (Math.abs(rdValor.nextInt() % 2)+1==1) {
                intX=2;
            }
            else
            {
               intX=-2;
            }
      }
      setX(getX()+intX);
      setY(getY()+intY);
    }
    public void Iniciar(){
         intX=0;
         intY=3;
    }
    public Enemigos (nFrames) {
         super(nFrames);
    }
}
Extendemos la clase y definimos un nuevo método para el movimiento, otros métodos para saber y establecer el estado y un método de inicialización de valores. Y ya hemos terminado nuestra nueva clase, que contiene todos los métodos y propiedades de la clase Sprite más los nuevos que acabamos de crear.

Modificaciones en programa principal
Ya tenemos la programación para crear objetos "enemigos". Vamos a crear una programación para mostrarlos de tal forma, que por cada 20 vueltas de nuestro bucle infinito aparezca un objeto enemigo, en una posición X aleatoria. Podemos tener varios a la vez, establecemos que un máximo de 6. Para ello haremos lo siguientes cambios.

Definir las siguientes variables globales:
int intEnemigoLibre;
int ciclos;
Enemigos[] arrEnemigos=new Enemigos[7];
Random rdValor=new Random();

En el método constructor de la clase SSCanvas, inicializamos las variables correspondientes:
public SSCanvas {
    //Inicializar baldosas
    ....
    //Control de enemigos
    ciclos=0;
    for (i=1;i<=6;i++) {
        arrEnemigos[i]=new Enemigos(1);
        arrEnemigos[i].Cargar_Frame(1,"ruta imagen");
        arrEnemigos[i].Off();
    }
}

Una vez inicializadas programamos lo que tiene que pasar en el bucle continuo:
public Run...{
    while (true) {
        //Control de enemigos
            if (ciclos==20) {
                ciclos=0;
                intEnemigoLibre=0;
                //Miramos si hay algún enemigo sin mostrar. Para no pasarnos del máximo de 6 a la vez
                for (i=1;i<=6;i++){
                    if (!arrEnemigos[i].Activado){
                        intEnemigoLibre=i;
                    }
                 }
                 if (intEnemigoLibre!=0) {
                     arrEnemigos[intEnemigoLibre].On();
                     //Mostramos al enemigo en una posX aleatoria dentro de la pantalla
                     arrEnemigos[intEnemigoLibre].SetX(Math.abs(rdValor.nextInt()%getWidth())+1);
                     arrEnemigos[intEnemigoLibre].SetY(0);
                     arrEnemigos[intEnemigoLibre].setEstado(1);
                     arrEnemigos[intEnemigoLibre].Iniciar();
                }
         }
         ciclos=ciclos+1;
         //Actualizamos el movimiento de los enemigos (si están visibles)
         for (i=1;i<=6;i++){
             if (arrEnemigos[i].Activado()) {
                 arrEnemigos[i].Calcular_Movimiento();
                 //Los hacemos desaparecer al llegar a los límites de la pantalla
                 if (arrEnemigos[i].getY()>getHeight() || arrEnemigos[i].getY()<0) {
                    arrEnemigos[i].Off;
                 }
         }

        //Resto de control del loop
        .....
    }
}

Para terminar, solo nos queda dibujar los enemigos activos en el método Paint...
void Paint (Graphics g){
    //Resto de programación baldosas
    ...
    //Control de enemigos
    for (i=1;i<=6;i++){
        if (arrEnemigos[i].Activado()) {
                 arrEnemigos[i].draw(g); 
        }
    }
}

Y listo. La potencia de todo esto, aparte de mostrar enemigos, es que hemos visto como extender clases y demostrado como la nueva clase Enemigos, también conserva las propiedades y métodos de la clase Sprite. Además de los nuevos, claro.

miércoles, 31 de marzo de 2010

Práctica

Os pongo [aquí] el enlace con la práctica. A ello!

miércoles, 3 de marzo de 2010

Mover sprites con el teclado


Explicación del código

Clase Demo1

No sufre ningún cambio respecto a la entrada anterior.

Clase SSCanvas

El principal cambio es añadir la capacidad de escuchar (leer) desde el teclado del ratón los eventos producidos por este (eventos son las acciones que puede producir un objeto, en el caso del teclado son cuando una tecla es pulsada, es soltada, etc). Con esto haremos que uno de los sprites (el que llamamos player) podamos moverlo por la pantalla en el sentido que le indiquemos por medio del teclado o joystick del movil. El otro cambio es crear un sprite más llamado player que será con el que interactuemos con el teclado y unas lineas más de codigo para actualizar su posición y el pintado en pantalla.

Clase Sprite

Aqui añadiremos más atributos para controlar el estado del desplazamiento (utilizado por player), otros atributos estáticos para identificar las teclas pulsadas y 3 métodos más. El primer método es computePlayer que controlara el movimiento del sprite cuando se utilice un teclado, luego dos métodos que que controlaran en que dirección debe desplazarse, el primero keyDown activa el desplazamiento y keyUp desactiva el desplazamiento.

Código fuente

/importamos las clases de java que utilizaremos en nuestro programa
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;


/*aqui nuestro MIDlet, esta clase sera la primera en ser ejecutada cuando se
ejecute el programa*/
public class Demo1 extends MIDlet {    
    //metodo cuando el midlet arranca
    public void startApp() {
        //creamos un objeto SSCanvas (nuestra pantalla de juego)
        SSCanvas screen=new SSCanvas();
        //indicamos al dispositivo que screen (instancia de SSCanvas) será la 
  //nueva pantalla activa
        Display.getDisplay(this).setCurrent(screen);   
        new Thread(screen).start(); //arrancamos el bucle (nuevo hilo de ejecución)
    }
    
    //metodo cuando el midlet entra en pausa
    public void pauseApp() {
    }
    
    //metodo cuando el midlet es destruido
    public void destroyApp(boolean unconditional) {
    }
}


//esta clase es la encargada de hacer funcionar nuestra aplicación gráfica 
class SSCanvas extends Canvas implements Runnable{ 
    private Sprite ball;
    private Sprite player;
    
    //metodo constructor
    public SSCanvas(){
        //instanciamos (creamos) el objeto ball a partir de la clase Sprite
        ball = new Sprite(getWidth()/2,getHeight()/2,20,20);
        player = new Sprite(getWidth()/2,getHeight()/3,30,30);
    }
    
    public void run(){
        while(true){
            //actualizamos la posicion de la bola
            ball.compute(getWidth(),getHeight());  
            player.computePlayer(getWidth(),getHeight());
            //forzamos el repintado de la pantalla
            repaint();
            serviceRepaints();
            try {
                Thread.sleep(30);            
            } catch (InterruptedException e) {        
            }
        }
    }
    //metodo de la clase canvas para pintar en pantalla
    public void paint(Graphics g){          
        //seleccionamos el color negro para pintar el fondo
 g.setColor(0,0,0);
 //pintamos el fondo de pantalla con un rectangulo con el tamaño de
 //la pantalla getWidth() y getHeight() es un método de la clase canvas
 //que nos devuelve el ancho y alto de la pantalla
        g.fillRect(0,0,getWidth(),getHeight());       
        //pintamos la bola en pantalla
        ball.draw(g);
        player.draw(g);
    }    
    
    /*sobrecarga de método para detectar cuando una tecla es soltada 
    por el usuario*/
    public void keyReleased(int keyCode) {
 int action=getGameAction(keyCode);  
 switch (action) {
            case LEFT:    
                player.keyUp(player.LEFT);
  break;
            case RIGHT: 
                player.keyUp(player.RIGHT);
  break;
            case UP:
                player.keyUp(player.UP);
  break;
            case DOWN: 
                player.keyUp(player.DOWN);
  break; 
            case FIRE:                
                break;            
        }
    }

    //sobrecarga del método que detecta cuando una tecla es pulsada por el usuario
    public void keyPressed(int keyCode) {
 int action=getGameAction(keyCode);         
        switch (action) {
            case LEFT:
                player.keyPress(player.LEFT);               
                break;
            case RIGHT:
                player.keyPress(player.RIGHT);  
  break;
            case UP:
                player.keyPress(player.UP);                
                break;
            case DOWN:
  player.keyPress(player.DOWN); 
  break;
            case FIRE:      
  break;                     
         }        
    }
}

class Sprite{
    //atributos privados
    protected int x; 
    protected int y;
    protected int width;
    protected int height;
    protected int incX,incY;
    private boolean startX=false;
    private boolean startY=false;
    
    //declaracion de constantes
    protected static final int LEFT = 1;
    protected static final int RIGHT = 2;
    protected static final int UP = 3;
    protected static final int DOWN = 4;
    protected static final int FIRE = 5;
    
    
    //metodo constructor 
    public Sprite(int x,int y,int width,int height){
        this.x=x;
        this.y=y;
        this.width=width;
        this.height=height;
        incX=3;
        incY=3;
    }
    
    //metodo que actualiza la posición del sprite
    public void compute(int screenW, int screenH){
        if(x <= 0)incX=3;
        else if(x+width >= screenW)incX=-3;
        
        x+=incX;
    }
    
    //metodo que actualiza la posicion del sprite que controlamos por teclado
    public void computePlayer(int screenW, int screenH) {
        if(x + incX > 0 && (x + incX) < (screenW - width)){
            x+=incX;             
            if(startX==false)incX=0;
        }
        if(y + incY > 0 && (y + incY) < (screenH - height)){
            y+=incY;
            if(startY==false)incY=0;
        }          
    } 
    
    //metodo para detener el desplazamiento del sprite
     public void keyUp(int keyCode) {
 switch (keyCode) {
            case LEFT:
  startX=false;    
  break;
            case RIGHT:
  startX=false;
  break;
            case UP:
  startY=false;
             break;
            case DOWN:
  startY=false;
  break;        
        }  
    }
 
    //método para indicar la dirección del desplazamiento
    public void keyPress(int keyCode) { 
        switch (keyCode) {
            case LEFT:
                incX=-5;
                startX=true;     
                break;
            case RIGHT:
                incX=5; 
                startX=true;   
                break;
            case UP:
                incY=-5;
                startY=true;
                break;
            case DOWN:
                incY=5;
                startY=true;
                break;
            case FIRE:                    
                break;
        }
    }   
    
    //metodo que muestra en pantalla al sprite
    public void draw(Graphics g){
        g.setColor(250,0,0);
        g.fillArc(x,y, width, height,0, 360);        
    }
}

Crear Sprites


EXPLICACION DEL CÓDIGO

Clase Demo1:

Esta clase es una extensión de la clase MIDlet, esto quiere decir que ésta clase creada por nosotros (clase Demo1) adquirira la funcionalidad de la clase MIDlet.
La Clase MIDlet es una clase del sistema, es decir viene dentro del paquete de java. Ésta clase se utilizará siempre en cualquier aplicación movil ya que es la primera que se lanza cuando se ejecuta un archivo de java para movil (archivo con extension .jar). Esta clase se encarga basicamente del control del movil.
Volviendo a nuestra clase Demo1. Siendo ésta una extensión de MIDlet será necesario introducir los métodos que controlan al MIDlet. Estos métodos son:
public void startApp()
public void pauseApp()
public void destroyApp(boolean unconditional)
El primero de ellos se lanza (ejecuta) cuando el MIDlet es ejecutado, el segundo cuando el MIDlet entra en pausa y el tercero cuando el MIDlet es destruido.
En fin, esta clase se encargará de arrancar la aplicación y de derivar a la siguiente clase (SSCanvas) el control de la pantalla y demás funcionalidades necesarias para nuestro juego.

Clase SSCanvas:

Esta clase es una extensión de la clase Canvas. Canvas es una clase de sistema que se encarga basicamente de la parte gráfica a bajo nivel. Con ello podremos acceder a todas las funcionalidades gráficas brindadas por java y el dispositivo movil. El método más importante de la clase Canvas es paint cuyo único parámetro es el objeto Graphics, éste método nos da la posibilidad de pintar en pantalla a través de la clase Graphics.
Nuestra clase SSCanvas además de ser una extensión de Canvas implemente un hilo independiente de ejecución, por medio de Runnable, con ésto conseguimos que nuestra aplicación tenga vida y podamos mover sprites por pantalla. Que lio no?. Hablando en cristiano, utilizando solo Canvas sin implementar Runnable es como sacar un foto, en cambio si implementamos Runnable esto sera más bien como un video, en donde las cosas se mueven por la pantalla.
Para implementar Runnable tendrémos que añadir un método obligatorio llamado run(). Déntro, nosotros pondremos el código necesario para que esto tome vida.

Clase Sprite:

Esta clase esta integramente creada por nosotros no implementa ni es una extensión de ninguna otra clase. Lo primero de todo será explicar que es un Sprite, aqui el concepto.
Sprite : es cualquier objeto que aparece en nuestro videojuego. Normalmente tiene asociado algunos atributos, siendo los más básicos una imagen y una posición.
Dicho esto, nuestro Sprite no tendrá una imágen sino que será una primitiva gráfica (circulo, cuadrado,lineas, etc), en este caso un circulo pintado de color rojo para ser exactos. Los atributos de este sprite son la posicion donde se mostrará (x, y) el ancho y alto que tendrá (width, height), y el incremento sobre el eje x (incX).
Además de atributos la clase Sprite tendrá tres métodos
- el primero será el método constructor (es el primer método que se lanza cuando es instanciada un objeto de esta clase) que espera los siguientes parámetros posicion inicial (x e y) y el ancho y alto que tendrá el sprite (width y height).
- el segundo será el método compute(int screenW, int screenH). como parametros espera el ancho y alto de pantalla y su función será la se mover la bola de un lado a otro de la pantalla.
- el tercero será el método draw(Graphics g), como parámetro espera el objeto graphics donde pintará a nuestro sprite. Este método solo servira para pintar nuestro sprite en el objeto graphics.

Codigo fuente

//importamos las clases de java que utilizaremos en nuestro programa
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;


/*aqui nuestro MIDlet, esta clase sera la primera en ser ejecutada cuando se
ejecute el programa*/
public class Demo1 extends MIDlet {    
    //metodo cuando el midlet arranca
    public void startApp() {
        //creamos un objeto SSCanvas (nuestra pantalla de juego)
        SSCanvas screen=new SSCanvas();
        //indicamos al dispositivo que screen (instancia de SSCanvas) será la 
  //nueva pantalla activa
        Display.getDisplay(this).setCurrent(screen);   
        new Thread(screen).start(); //arrancamos el bucle (nuevo hilo de ejecución)
    }
    
    //metodo cuando el midlet entra en pausa
    public void pauseApp() {
    }
    
    //metodo cuando el midlet es destruido
    public void destroyApp(boolean unconditional) {
    }
}


//esta clase es la encargada de hacer funcionar nuestra aplicación gráfica 
class SSCanvas extends Canvas implements Runnable{ 
    private Sprite ball;
    
    //metodo constructor
    public SSCanvas(){
        //instanciamos (creamos) el objeto ball a partir de la clase Sprite
        ball = new Sprite(getWidth()/2,getHeight()/2,20,20);
    }
    
    public void run(){
        while(true){
            //actualizamos la posicion de la bola
            ball.compute(getWidth(),getHeight());
            //forzamos el repintado de la pantalla
            repaint();
            serviceRepaints();
            try {
                Thread.sleep(30);            
            } catch (InterruptedException e) {        
            }
        }
    }
    //metodo de la clase canvas para pintar en pantalla
    public void paint(Graphics g){          
        //seleccionamos el color negro para pintar el fondo
 g.setColor(0,0,0);
 //pintamos el fondo de pantalla con un rectangulo con el tamaño de
 //la pantalla getWidth() y getHeight() es un método de la clase canvas
 //que nos devuelve el ancho y alto de la pantalla
        g.fillRect(0,0,getWidth(),getHeight());       
        //pintamos la bola en pantalla
        ball.draw(g);
    }    
}

class Sprite{
    //atributos privados
    protected int x; 
    protected int y;
    protected int width;
    protected int height;
    protected int incX;
    
    //metodo constructor 
    public Sprite(int x,int y,int width,int height){
        this.x=x;
        this.y=y;
        this.width=width;
        this.height=height;
        incX=3;
    }
    
    //metodo que actualiza la posición del sprite
    public void compute(int screenW, int screenH){
        if(x <= 0)incX=3;
        else if(x+width >= screenW)incX=-3;
        
        x+=incX;
    }
    
    //metodo que muestra en pantalla al sprite
    public void draw(Graphics g){
        g.setColor(250,0,0);
        g.fillArc(x,y, width, height,0, 360);
    }
}

viernes, 12 de febrero de 2010

Ejercicio de repaso - Moviles

Hacer una aplicación para movil que haga que una pelota rebote por los límites de la pantalla.

Aplicaciones para moviles - Segundo Parcial

Hoy comenzamos las clases del segundo parcial.
Vamos a crear una aplicación para moviles que hereda de MIDlet. En Netbeans, hay que seleccionar crear una nueva aplicacíón JME, no seleccionar lo de hello y luego crear una nueva librería del tipo MIDLet. (no visual).

Un ejemplo de código es este:

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());
    g.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 utilizamos un derivado de la clase SSCanvas que implementamos justo debajo.
private SSCanvas screen;

La clase SSCanvas hereda de la clase Canvas. Es por ello que podemos utilizarla como pantalla principal.
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) {

viernes, 11 de diciembre de 2009

Ejercicios de repaso - Gráficos (2)

Ejercicio 1: Realizar el siguiente ejercicio:

Ejercicio 2: Hacer el juego del ahorcado, tal y como lo hicimos en prácticas anteriores. Solo que ahora en modo gráfico.