martes, 15 de mayo de 2012

Utilizando el Framework AndEngine.


Primeros pasos en la creación de Vídeos Juego 


 Hola todos, en esta serie de artículos, intentaremos dar una introducción al desarrollo de vídeos juegos sobre la plataforma Android, para esto utilizaremos la librería AndEngine, que es un Engine 2D que utiliza OpenGl. Esta librería la podemos bajar desde esta url: 


 (para iniciar la descarga realicen un click sobre el [1])

Ahora lo primero que deberemos realizar sera crear un proyecto Android, (supongo que ya tendrán instalado el SDK de Android y un IDE, en mi caso Eclipse ). Como ya sabrán para esto deberemos abrir Eclipse, ir a la opción File -> New -> Android Project.


 Una vez creado el proyecto, le realizamos click botón derecho sobre el mismo, vamos a New -> Folder, y creamos una carpeta con el nombre lib. Donde pegaremos el archivo andengine.jar que nos bajamos del enlase antes mencionado, luego click derecho sobre el archivo elegimos la opcion Build Path -> Add to Build Path, si todo fue bien nos deberá aparecer en Referenced Libraries.


 Antes de comenzar con lo divertido, o sea ver algo de código, creo que nos resultara de mucha utilidad comprender algunos conceptos que pasare a explicar:

BaseGameActivity: El BaseGameActivity es la raíz del juego, que contiene el motor y crea la vista donde se va a dibujar todo. Hay siempre exactamente un solo Engine por cada BaseGameActivity.

Engine: El Engine es el motor interno del juego, se encarga de ir dibujando en pantalla y actualizando objetos en la escena, que contiene todo el contenido que tu juego lleva. Normalmente hay una escena por Engine, a menos que vayas a usar un SplitScreenEngines. (por ahora esto no lo veremos).

IResolutionPolicy: Una implementación de IResolutionPolicy interface es parte del EngineOptions. Te hace abstraerte de la resolución del terminal, así tú trabajas solo para una resolución y el AndEngine se encarga del resto.

Camera: Un objeto Camera define el rectángulo visible actualmente de la escena actual, no tiene porqué ser la escena completa. Normalmente hay una cámara por escena. Hay subclases específicas que permiten hacer zoom y mover la cámara suavemente.

Scene: La clase Scene es el contenedor para todos los objetos que se van a dibujar en la escena. Una escena puede tener Layers, que son capas para ordenar objetos. Hay subclases de la Scene como CameraScene/HUD/MenuScene que tienen comportamientos específicos.

Entity: Una entidad es un objeto que puede ser dibujado, como imágenes, rectángulos, texto, líneas. Una entidad tiene posición/rotación/zoom/color/ etc, etc.

Texture: Una textura es una imagen que se guarda en memoria. En Android, las imágenes deben ser una potencia de 2.
ITextureSource: Una implementacion de ITextureSource-interface se encarga de cargar una imagen en una posición en la textura.
TextureRegion: Una TextureRegion define un rectángulo en una imagen. Las TextureRegion se usan por Sprites para usar una imagen grande en la que guardamos muchas imagenes pequeñas.

PhysicsConnector: Motor de físicas integrado en el Engine.

Ahora sí, para que nos queden mejor estos conceptos, creo que hay que verlo en el código, que es lo que pasaremos a realizar ahora. Como ya sabrán uno en un proyecto Android esta acostumbrado a heredar de Activity, en este caso deberemos modificar esto para extender de BaseGameActivity. Para que quede mas claro, veamos como queda la clase:

public class AndEnginePrimerasPruebasActivity extends BaseGameActivity {
public Engine onLoadEngine() {
return null;
}

public void onLoadResources() {
}

public Scene onLoadScene() {
return null;
}

public void onLoadComplete() {
}
}


 Como podran observar, al heredar de BaseGameActivity debemos implementar una serie de metodos, los mismos son:

onLoadEngine: Esta función se ejecuta al principio del todo y sirve para configurar aspectos del motor como la cámara, la orientación de la pantalla, etc.

onLoadResources: Desde esta función podremos cargar las imágenes, música, sonidos, fuentes, o cualquiera de los recursos que vallamos a necesitar.

onLoadScene: Esta función ya ejecuta el juego en si. Aquí tenemos que inicializar el motor físico, todos los loops, nuestras clases de personaje, enemigos, mapas, etc. Si nos fijamos, los juegos con AndEngine funcionan por escenas, a la clase Scene se le tienen que añadir los loops, como el de la física, y todos los elementos que queremos ver por pantalla.

onLoadComplete: Esta función se ejecuta cuando el juego ha acabado de cargar todo. Hasta ahora en los manuales y documentación leída no se le a dado demasiada importancia (por lo menos en un comienzo, ya lo veremos para cosas mas complejas ).

Ahora deberemos proceder a crear un objeto de tipo ZoomCamera, y crearemos dos variables de tipo int, que luego le pasaremos a ZoomCamera. Básicamente esto sera la pantalla del juego, y los parámetros int serán el ancho y largo de pantalla.

private static final int CAMERA_WIDTH = 725;
private static final int CAMERA_HEIGHT = 490;
private ZoomCamera mCamera;

Al programar en OpenGl tenemos el beneficio de que no importa el ancho y el largo que le queramos dar, pues OpenGl lo redimencionara hacia el tamaño de pantalla de nuestro móvil, sin que se que se pierda la escala de las figuras que incorporemos. Lo que si deberemos tener cuidado es si queres que la pantalla del juego ocupe la totalidad de la misma, o si deseamos dejarles alguna franja para colocar alguna otro imagen, o lo que deseemos. ( También para que se vea de manera correcta en todas las pantallas de celular podemos utilizar otras técnicas, pero eso lo veremos mas adelante)

Ahora, implementaremos el método onLoadEngine de la siguiente manera:

@Override
public Engine onLoadEngine() {
this.mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE,
new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), this.mCamera));
}

Como verán dentro de este método nosotros crearemos nuestra cámara con los parámetros de tamaño que antes habíamos creado, también como lo requiere el método le devolvemos el Engine con la orientación que nosotros queremos, en este caso horizontal (Landscape).




Y con RatioResolutionPolicy lo que haremos sera que la imagen no se distorsioné o deforme cuando ponemos al juego en móviles con pantallas que tienen tasas de densidades diferentes. (ya que si no hacemos esto, nos podrían por ejemplo aparecer bandas negras para terminar de rellenar la pantalla).

Ahora pasaremos al siguiente método a implementar, en este caso onLoadScene:

public Scene onLoadScene() {
final Scene scene = new Scene(1);
scene.setBackground(new ColorBackground(1f, 1f, 1f));
return scene;
}


Lo utilizaremos para crear todos los objetos que queremos que aparecerán en nuestro juego, en este caso simplemente creamos una scena vacía, y seteamos en color de fondo en blanco. Esto lo realizamos con ColorBackground donde simplemente sirve para establecer un color simple de fondo pasándole solo los porcentajes de color que deseemos de cada uno (RGB = rojo, verde, azul) con números que van desde 0 a 1, o sea que 0,25 seria un valor correcto.
También hay maneras mas avanzadas de establecer un fondo, como pueden ser:

EntityBackground: Establecemos una entidad (algo que se puede dibujar) como fondo. Como normalmente lo que vamos a utilizar serán sprites, en vez de esta clase, utilizaremos SpriteBackground, que la hereda.

SpriteBackground: Para establecer como fondo un sprite (una imagen).

RepeatingSpriteBackground: Para establecer un fondo con una imagen que se repite hasta rellenar la pantalla por completo.

ParallaxBackground: Para añadir varias capas de fondo y hacer scroll parallax controlado por nosotros.
AutoParallaxBackground: Igual que el anterior, sólo que estará siempre en movimiento.

Ya con esto, si quieren probar ( si tienen un emulador funcionando o simplemente un móvil conectado vía USB) pueden darle click derecho a nuestro proyecto, van a Run As -> Android Application. Y podrán ver lo que hemos creado, una simple pantalla blanca :D . Por supuesto que esto es solo el comienzo ;).


  

 Para seguir les daré un consejo, que les puede llegar a resultar muy util, vamos a nuestro AndroidManifest y debajo de la linea de código:

<uses-sdk android:minSdkVersion="4" />

Es conveniente que agreguen este permiso para nuestra aplicación:

<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>

De esta manera la suspención de nuestro celular no nos molestara, lo cual en un juego puede resultar realmente molesto y poco “amigable para el usuario”.


Agregango un objeto.

Pasemos a algo mas interesante ahora, estoy seguro que ya desean agregar algún objeto en pantalla, ya que se ve muy aburrida. Esto a varios les llamara la atención, pero para agregar imágenes en AndEngine no se va a utilizar la clase R auto-generada, así que dentro de la carpeta assets crearemos otra llamada gfx. Una vez creada la carpeta, deberemos agregar una imagen a la misma ( una imagen que deseemos agregar como objeto a nuestra aplicacion y que se vera en nuestra Scene ).
Ahora lo que deberemos hacer es crear el objeto en nuestro código, de esta manera:

private BitmapTextureAtlas mTexture;
private TextureRegion mFaceTextureRegion;

Pasemos a explicar, BitmapTextureAtlas es un tipo de Texture, la misma es una imagen que es cargada desde el disco hacia el chip gráfico. Hay que tener en cuenta , cuando se instancie, que en OpenGl el valor del ancho y alto deben ser una potencia de dos. Y no necesariamente deben ser cuadradas, si no que también pueden ser rectangular, por ejemplo : 32 x 256.
Para poder cargar en nuestro juego imágenes donde el tamaño no este definido por números que sean potencia de dos, debemos utilizar el TextureRegion define un rectángulo en una textura y es utilizado por los Sprites para que el sistema sepa que rectángulo de la textura grande contiene la imagen que representa.
Un Sprite representa a un elemento del juego: El personaje, el fondo, los enemigos, las explosiones, etc. Podemos crear Sprites sin animación (Sprite), sprites con varios estados controlados con código (TiledSprite) o animados (AnimatedSprite). Estos contendrán información sobre su posición (x, y), aceleración, velocidad, rotación etc. de tal forma que sepan donde dibujarse y cómo en cualquier momento. Todo esto se verá posteriormente.


Cuando vamos a cargar las imágenes en memoria, debemos modificar el metodo onLoadResources() de esta manera:

@Override
public void onLoadResources() {
this.mTexture = new BitmapTextureAtlas(64, 64,
TextureOptions.BILINEAR_PREMULTIPLYALPHA);
this.mFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mTexture, this, "gfx/pelotatenis.jpg", 0, 0);
this.mEngine.getTextureManager().loadTexture(this.mTexture);
}





Cuando utilizas imágenes que contienen transparencias puedes encontrarte con que te aparezcan "halos" que rodean los bordes de las mismas. Aquí es donde entra en juego la técnica PREMULTIPLYED ALPHA, la cual corrige ese problema. Intenta evitar utilizarlo a menos que veas cosas raras en los bordes de tus imágenes, como sombras o halos resplandecientes que no deberían estar. Si quieres una interpolación bilineal, estableces:
TextureOptions.BILINEAR
Si quieres una interpolación bilineal con PREMULTIPLYED ALPHA, estableces:
TextureOptions.BILINEAR_PREMULTIPLYALPHA
Es recomendable que explores todas las opciones de TextureOption, esto es solo un consejo.

Y por ultimo debemos cargar el método onLoadScene() con el siguiente código, para poder cargar la imagen en el centro de nuestra Scena :

public Scene onLoadScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
final Scene scene = new Scene(1);
scene.setBackground(new ColorBackground(0f, 1f, 0f));
final int centerX = (CAMERA_WIDTH - this.mFaceTextureRegion.getWidth()) / 2;
final int centerY = (CAMERA_HEIGHT - this.mFaceTextureRegion.getHeight()) / 2;
final Sprite ball = new Sprite(centerX, centerY, this.mFaceTextureRegion);
scene.getLastChild().attachChild(ball);
return scene;
}

Un dato, al agregar alguna imagen, si esta es excesivamente grande, le puede traer problemas, yo recomiendo que la escale a un tamaño menor, hay otras formas de tratarlas, pero es un poco mas complejo y se escapa a esta introducción ;)

Después de realizar esto, ya deberíamos poder ver la imagen en nuestro programa Android sin problemas, solo nos queda agregar un poco de movimiento, que sera lo ultimo que veremos en este articulo.



Vamos a movernos un poco:

Para esto vamos a necesitar que nuestra clase implemente dos interfaces, que seran:

IScrollDetectorListener: Nos obliga a que implementemos el método para controlar el scroll.

IOnSceneTouchListener: Y con esta clase debemos implementar el método para controlar la pantalla táctil.

Solo implementaremos los metodos que nos obligan esas interfaces, que serian estos:


public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent,
float pDistanceX, float pDistanceY) {
}
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
return true;
}


Deberemos modificar nuevamente onLoadScene() para que quede de esta manera , para que el engine pueda escuchar los eventos de la pantalla:


@Override
public Scene onLoadScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
final Scene scene = new Scene(1);
scene.setBackground(new ColorBackground(0, 0, 0.8784f));
scene.setOnAreaTouchTraversalFrontToBack();
this.mScrollDetector = new SurfaceScrollDetector(this);
this.mScrollDetector.setEnabled(true);
final int centerX = (CAMERA_WIDTH - this.mFaceTextureRegion.getWidth()) / 2;
final int centerY = (CAMERA_HEIGHT - this.mFaceTextureRegion
.getHeight()) / 2;

final Sprite ball = new Sprite(centerX, centerY,
this.mFaceTextureRegion);
scene.getLastChild().attachChild(ball);
scene.setOnSceneTouchListener(this);
scene.setTouchAreaBindingEnabled(true);
return scene;
}


Como se ve en el código, se han agregado los listener y el control del movimiento a la Scene, de esta manera podremos interactuar con ella. Una vez realizado esto, vamos a ver como modificamos los metodos para que nos funcione correctamente esta primera aplicación de prueba utilizando AndEngine.



public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent,
float pDistanceX, float pDistanceY) {
this.mCamera.offsetCenter(-pDistanceX, -pDistanceY);
}


Con esto que agregamos, lo que hacemos es decirle donde queremos que se centre al comienzo nuestra camara.

Y onSceneTouchEvent():


@Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
return true;
}



Acá vamos detectando donde se realizo el touch en la pantalla.

Por ultimo debemos volver a modificar el metodo onLoadEngine():


@Override
public Engine onLoadEngine() {
this.mCamera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
final int alturaTotal = CAMERA_HEIGHT*3;
final int anchoTotal = CAMERA_WIDTH*5;
this.mCamera.setBounds(0, anchoTotal, 0, alturaTotal);
this.mCamera.setBoundsEnabled(true);
return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE,
new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT),
this.mCamera));
}


En realidad sin estas molificaciones la aplicación, como podrán observar, se puede utilizar sin ningún tipo de error, el inconveniente es que al correr la pantalla no existen limites, y como en todo juego deberían de existir los mismo. Así que si lo prueban ahora, verán que esta restringida parte de la pantalla, mas adelante veremos como hacer que el espacio de la aplicación sea solo la parte visible de la pantalla o no.

Eso es todo por ahora, ya tiene un comienzo de lo que se puede llegar a realizar sin mucho código, piensen que solo tenemos una clase y la librería de AndEngine, para darles mas ánimos, acá les dejo un enlase donde contiene una lista de programas realizados con esta librería:


Espero que les allá gustado este articulo, en próxima ediciones podremos profundizar mas sobre esta librería, también les quería comentar que otra librería muy importante para la creación de vídeos juegos y de la que podemos llegar a hablar mas adelante es LibGDX, pero eso ya sera otra historia.


Saludos a todos, Gabriel E. Pozo




martes, 8 de mayo de 2012

Java, como agregar una imagen en un Applet.


Desarrollo sobre la plataforma Java:



Hola a todos, pues este es un post simple, donde mas que nada en el código fuente, de manera documentada mostrare como cargar imágenes en Java, en un Applet, de todas maneras solo muestro la manera mas sencilla de realizarlo.

package com.jackgris.cargarimagenes;

// importaciones necesarias

import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JApplet;


/*
* Creamos una clase que extienda de JApplet
*
*/
@SuppressWarnings("serial")
public class CargarYEscalaDeImagen extends JApplet{
/*
* Creamos a las variables que vamos a utilizar en nuestro Applet para cargar las imagenes
*/
private Image primeraImagen;
private ImageIcon segundaImagen;
/*
*Ahora en el metodo init, cargarmos las imagenes que vamos a utilizar en el Applet.
* Como pueden observar en el package Explorer, las imagenes estan agregadas en la carpeta
* src, por este motivo no debemos agregar ni una URL de la ubicacion de las imagenes,
* o el PATH absoluto de donde se encuentran. Recuerden que si esa no es la ubicacion de las
* imagenes, como por ejemplo que esten alojadas en un servidor WEB, en un archivo JAR o en
* algun otro lugar, no funcionara de esta manera. Y deberan espesificar la ubicacion de las
* imagenes de otra forma.
*/
public void init(){
primeraImagen = getImage(getDocumentBase(),"gtugargentina.jpg");
segundaImagen = new ImageIcon("gtugicon.png");
}
/*
* Ahora con el metodo paint, mostramos las imagenes en nuestro Applet. Tambien como veran en
* la imagen, la primeraImagen, es cargada en el Applet en dos lugares diferentes, y con tamaños
* diferenetes. Eso se debe a los parametros que le pasamos al metodo drawImage()
*
*/
public void paint(Graphics g){
g.drawImage(primeraImagen, 0, 0, this);
g.drawImage(primeraImagen, 0, 120, getWidth(), getHeight() -120 , this);
segundaImagen.paintIcon(this, g, 180, 0);
}

}

En esta imagen se puede ver donde se ubican las imagenes en el proyecto:



Este es el resultado, visto, desde el Visor de Applet, que no levanta automaticamente Eclipse, aunque lo podemos realizar de manera manual.




Como veran, este fue un post simple, como se diría cortito y al pie, mas adelante, veremos cosas mas avanzadas en Java, y algún que otro Framework, espero que les sea un tip útil.



Saludos a todos, Gabriel E. Pozo