Introducing
Your new presentation assistant.
Refine, enhance, and tailor your content, source relevant images, and edit visuals quicker than ever before.
Trending searches
Posee un grupo de APIs muy poderosas que ayudan en tareas de desarrollo de juegos como por ejemplo renderizar sprites, texto, crear interfaces de usuario, reproducir efectos de sonido y música, realizar cálculos algebraicos y trigonométricos, parsear a JSON y XML, entre otras.
Los widgets de UI no setean su medida y posición. Es el widget padre quien setea la medidas y posición de sus hijos. Los Widgets proveen medidas mínimas, preferidas, y máximas, que el padre usar como “pistas”.
Existen muchas clases para la creación de UI utilizando scene2d. Algunas son:
Table: Es un WidgetGroup que setea la medida y posición de sus hijos utilizando una tabla lógica, como las blas HTML.
Label: Muestra texto utilizando un BitmapFont y un color.
Image: Muestra un Drawable, que puede ser una textura, una región,un sprite, etc.
Button: Se trata de un botón vacío. Al extender de Table, se le pueden añadir otros widgets.
TextButton: Extiende de Button y contiene un label.
ScrollPane: Permite scrollear un widget hijo con scrollbars y/o arrastrando el mouse o el dedo.
Existen muchos otros widgets como por ejemplo ImageButton, CheckBox, ButtonGroup, TextField, Stack, List, SelectBox, Slider, SplitPane, Window, Touchpad, Tree, Dialog, entre otros.
public class SingleTouchHandler implements TouchHandler {
boolean isTouched;
int touchX,touchY;
Pool<TouchEvent> touchEventPool;
List<TouchEvent> touchEvents=new ArrayList<TouchEvent>();
List<TouchEvent> touchEventsBuffer=new ArrayList<TouchEvent>();
float scaleX, scaleY;
public SingleTouchHandler(View view,float scaleX,float scaleY ) {
PoolObjectFactory<TouchEvent> factory=new PoolObjectFactory<TouchEvent>() {
@Override
public TouchEvent createObject() {
return new TouchEvent();
}
};
touchEventPool=new Pool<TouchEvent>(factory,100);
view.setOnTouchListener(this);
this.scaleX=scaleX;
this.scaleY=scaleY;
}
@Override
public boolean onTouch(View view, MotionEvent event) {
synchronized (this) {
TouchEvent touchEvent=touchEventPool.newObject();
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
touchEvent.type=TouchEvent.TOUCH_DOWN;
isTouched=true;
break;
case MotionEvent.ACTION_MOVE:
touchEvent.type=TouchEvent.TOUCH_DRAGGED;
isTouched=true;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
touchEvent.type=TouchEvent.TOUCH_UP;
isTouched=false;
break;
}
touchEvent.x=touchX=(int)(event.getX()*scaleX);
touchEvent.y=touchY=(int)(event.getY()*scaleY);
touchEventsBuffer.add(touchEvent);
}
return true;
}
@Override
public boolean isTouchDown(int pointer) {
synchronized (this)
{
if(pointer==0)
{
return isTouched;
}
else
{
return false;
}
}
}
@Override
public int getTouchX(int pointer) {
synchronized (this)
{
return touchX;
}
}
@Override
public int getTouchY(int pointer) {
synchronized (this)
{
return touchY;
}
}
@Override
public List<TouchEvent> getTouchEvents() {
synchronized (this)
{
int len=touchEvents.size();
for(int i=0;i<len;i++)
{
touchEventPool.free(touchEvents.get(i));
}
touchEvents.clear();
touchEvents.addAll(touchEventsBuffer);
touchEventsBuffer.clear();
return touchEvents;
}
}
}
Su estructura es muy intuitiva y entendible, por lo que no toma mucho tiempo aprender a utilizar la herramienta. Además, existe mucha documentación que ayuda al aprendizaje.
Por último, cabe destacar que se trata de un framework gratuito para programar en Java y permite que cualquiera que esté interesado pueda crear una aplicación.
Si un programador esta centrado en el desarrollo puro de juegos para Android no hay razones para que desarrolle el juego con el SDK cuando tiene disponible un framework mucho más superior como LibGDX.
• OpenGL:
http://www.opengl.org/about/
• mpg123:
http://www.mpg123.de/
• SoundTouch Audio Processing Library:
http://www.surina.net/soundtouch/
• Box2D:
http://box2d.org/about/
• OpenAL:
http://www.openal.org/
Este es solo un ejemplo muy reducido de la potencialidad que tiene el uso de un framework como LibGDX.
En segundo lugar, LibGDX es un framework muy completo, proporcionando todas las herramientas necesarias para comenzar con el desarrollo de su juego y permitiendo darle toques de calidad profesional de forma muy simple.
En tercer lugar, una de las principales ventajas de LibGDX es que mientras se desarrolla el juego para Android, se puede generar y probar en la misma PC con Windows, Linux o Mac OS. Esto significa que se ahorrará mucho tiempo en comparación a los lentos ciclos de "build and deploy" que tiene el desarrollo de aplicaciones Android.
• Programación de videojuegos en Android con LibGDX:
https://docs.google.com/document/d/1YfuJ-gsc7VfIlX2G8Ix5K0laaqEsvFDuTz3Cnecy2_M/edit?pli=1
• LibGDX en Español:
http://libgdxspain.blogspot.com.ar/
• LibGDX wiki:
https://github.com/libgdx/libgdx/wiki
• Lightweight Java Game Library:
http://www.lwjgl.org/
Ahora veamos un ejemplo de un manejador de eventos que tendríamos que crear por nuestra cuenta si no usáramos el framework, es decir, usando solo el SDK de Android:
"Imaginemos un carpintero que cada vez que tiene que construir un mueble debe – además- fabricar por sí mismo cada una de las herramientas que necesita. Para esto perdería mucho tiempo en adquirir conocimientos y habilidades que no le conciernen. Además, probablemente no llegue a cumplir con los plazos de entrega y los clientes se quejen."
Sabiendo de la existencia de estas herramientas (que seguramente sean de una calidad muy superior a las fabricadas por el carpintero) ...suena ilógico todo esto, no?
Es, sin duda alguna, mucho más fácil y práctico aprender a usar el framework que tener que aprender una cantidad enorme de conocimientos y código complejos. Al fin y al cabo desarrollar un videojuego no es una tarea sencilla, y tener las herramientas necesarias nos permite focalizarnos en el desarrollo del videojuego en sí, no en el desarrollo de la herramienta.
Se trata de una clase simple que describe un rectángulo bidimensional, descripto por su borde inferior izquierdo, alto y ancho. Provee la habilidad de testear si un punto está contenido en el rectángulo. También se puede consultar si hay intersección con otros rectángulos o círculos utilizando la clase Intersector.
Para desarrollar un videojuego usando solo las herramientas nativas de Andriod (sin LibGDX) primero tendríamos que adquirir un conocimiento sustancial sobre el propio SDK (kit de desarrollo de software) de Android. Esto significaría que inicialmente vamos a estar concentrados en el SDK en lugar del propio desarrollo del juego.
En primer lugar, LibGDX te oculta y abstrae de todo el código nativo subyacente del SDK de Android. Significa que el programador no va a ver ni una mínima parte del SDK mientras desarrolla su juego. Esto deriva en una menor curva de aprendizaje para ponerse en marcha con los distintos conceptos que involucra el desarrollo de juegos (teniendo en cuenta que el programador no tiene experiencia previa con eso).
Uno de los patrones que utiliza LibGDX es el encadenamiento de métodos para reducir la escritura de código. Cada operación que modifica un vector retorna una referencia a ese vector para poder continuar encadenando operaciones en la misma línea de llamadas. Por ejemplo:
Vector posicion = new Vector2(x, y).sub(10, 20).nor();
En el juego se utiliza tanto Rectangle como Vector2 en las entidades del juego.
Esta primera clase representa los bordes de la entidad, y la última, la posición en el mapa:
public class ObjetoJuego {
protected Vector2 posicion;
protected Rectangle limites;
public ObjetoJuego (float x, float y, float ancho, float alto) {
this.posicion = new Vector2(x, y);
this.limites = new Rectangle(x - ancho / 2, y - alto / 2, ancho, alto);
}
LibGDX posee varias clases geométricas para el manejo de formas, áreas y volúmenes, incluyendo:
Circle, Frustum, Plane, Spline, Polygon, Ray y Rectangle.
En este proyecto se hablará sólo de Rectangle, ya que es la forma geométrica empleada para la implementación del proyecto, y el resto son similares, variando la forma geométrica.
Un vector es un arreglo de números utilizado para describir un concepto con una dirección y magnitud como posición, velocidad o aceleración. Estos generalmente son usados para describir las propiedades físicas de movimiento de un cuerpo en el espacio. LibGDX tiene clases para vectores bidimensionales (Vector2) y tridimensionales. (Vector3). Estos contienen operaciones como adición, substracción, normalización y el cruce de puntos.
Una evidencia de esto es el siguiente ejemplo:
Este es el código necesario para la manipulación de eventos touch usado en el juego:
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
Solo necesitamos importar las librerías necesarias de LibGDX y luego implementarlas en el juego. En el siguiente fragmento de código se agrega un manejador de eventos touch a un botón del menú principal:
botonJugar.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
((Juego)Gdx.app.getApplicationListener()).setScreen(new SeleccionNivel(juego));
return true;
};
}
);
Como implementación de esta investigación, se creó “Ninjump”, un juego 2D que se puede ejecutar tanto en dispositivos con Android como en desktop.
El objetivo del juego es hacer que el ninja, personaje controlado por el usuario, llegue al portal que se encuentra en la cima de cada nivel, tratando de obtener el mayor puntaje posible.
El ninja salta verticalmente de forma automática y constante, y es controlado por medio del acelerómetro en dispositivos con Android, o con las flechas del teclado en desktop para moverlo lateralmente, y por medio de la pulsación de la pantalla táctil en dispositivos con Android o el click del mouse en desktop para que lance ataques contra sus enemigos.
Para llegar al portal, el ninja debe saltar sobre las plataformas para ascender, y evitar caerse o tocar un fantasma ya que si algo de esto sucede, el jugador pierde la partida, y todo el progreso del nivel. Las plataformas pueden romperse luego de que el ninja salte sobre ellas. Si el personaje sale de la pantalla lateralmente, aparece en el extremo opuesto.
También existen otros ítems que puede obtener el personaje:
Monedas: Si el ninja toca una moneda, el jugador suma diez puntos.
Shurikens: Si el personaje toca este ítem, obtiene estrellas ninjas para atacar a sus enemigos. Por cada enemigo eliminado, el jugador suma cinco puntos.
Poder: Si el ninja salta sobre un poder, su próximo salto será más alto.
Dependiendo del puntaje logrado, el jugador obtendrá una cantidad de estrellas por cada nivel completado, siendo el mínimo una estrella y el máximo tres. Estas se pueden ver en la pantalla de selección de nivel, debajo de cada botón.
El progreso del juego y las preferencias del jugador persisten en un archivo en la memoria externa si el juego es ejecutado en Android, o en la carpeta de usuario si es ejecutado en desktop.
El juego posee pantallas de ayuda para que el usuario aprenda a jugar, y una pantalla de opciones donde puede habilitar o deshabilitar los efectos de sonido y/o la música.
El usuario interactúa a través de diferentes dispositivos dependiendo de la plataforma en la que esté ejecutando el juego:
• Android: A través del acelerómetro, el usuario controla al personaje para moverlo lateralmente. El resto de la interacción con el juego se lleva a cabo por medio de la pantalla táctil: cambiar de pantallas, seleccionar nivel, poner en pausa el juego, salir, etc.
• Desktop: Al no disponer de acelerómetro en escritorio, el usuario controla al personaje por medio de las flechas laterales del teclado físico. El click izquierdo del mouse es equivalente al uso de la pantalla táctil en Android.
Para gestionar la entrada por parte del usuario, se utiliza tanto el mecanismo de polling, como InputProcessor para el manejo de eventos, utilizando el patrón observer.
Módulo empleado para obtener y utilizar los archivos internos, es decir, los recursos del juego: la música, los efectos de sonido, y las imágenes.
También se lo utiliza para guardar y cargar el progreso y preferencias del usuario en archivos externos, es decir, en la carpeta del usuario en desktop y en la tarjeta SD en Android. Todo esto en unas pocas líneas de código.
Utilizada para reproducir o pausar los efectos de sonido empleando la clase Sound, y la música del juego utilizando la clase Music.
A través de este módulo se accede al contexto de OpenGL para setear el color de fondo de las pantallas y limpiarlas antes de cada llamada a render()
El método getDeltaTime() resulta de gran utilidad para renderizar las imágenes y modificar el estado de entidades del modelo.
Se utilizan las clases Texture, TextureRegion y TextureAtlas para representar imágenes y regiones de imágenes.
Las animaciones de las entidades del juego son generadas con la clase Animation.
Se emplea la clase OtrhographicCamera para mostrar sólo el sector del mapa del juego en donde se encuentra el personaje, y para setear la medida de la ventana para mostrar el contenido.
Para las imágenes de los botones del menú principal se utilizan Ninepatches que se adaptan a la medida del botón
Se emplea la clase Pixmap leer cada píxel de las imágenes que representan cada nivel, y así poder generarlos.
Se utilizó Scene2d.ui para crear botones con estilos, la pantalla deslizable de selección de niveles, tablas para ordenar el contenido de las pantallas y labels.
Fue de gran utilidad para el manejo de los assets la clase AssetManager, en donde se los puede cargar, y desde donde se puede acceder a ellos para su uso luego de ser cargados.
Fueron muy útiles los rectángulos para determinar los límites de entidades y chequear si colisionan entre ellas, y vectores para determinar por ejemplo la gravedad del mundo, o la posición de las entidades.
Para poder diferenciar qué dispositivos utilizar dependiendo de la plataforma, también fue necesario utilizar el campo app de la clase estática Gdx para realizar consultas sobre la aplicación, como por ejemplo en que plataforma se está ejecutando y qué periféricos de entrada se encuentran disponibles.
Fue de gran ayuda la API de JSON que brinda LibGDX, la cual permite escribir los archivos en este formato, y luego poder leer los datos y crear objetos a partir de ellos para que puedan ser interpretados por el juego. Además se encriptaron los datos persistidos utilizando la clase Base64Encoder, para evitar que los datos dentro de los archivos pudieran ser fácilmente manipulados.
El desarrollo de juegos para Android puede ser muy complejo, pero actualmente existen frameworks que pueden hacer que sea mucho más simple. Uno de los más conocidos, y más utilizados por los desarrolladores es LibGDX.
En este trabajo se tratará de contestar al siguiente interrogante:
¿Es el Framework LibGDX una herramienta que nos facilita el trabajo a la hora de desarrollar juegos para Android?
• El proyecto de Android: Tendrá el nombre que especificamos para el proyecto, con el sufijo "-android". Contiene la clase iniciadora y otros archivos necesarios para ejecutar la aplicación en Android. Dentro, contiene una carpeta llamada assets/, donde se almacenan los recursos de la aplicación para todas las plataformas.
• El proyecto del core: Tendrá el nombre que especificamos para el proyecto, sin sufijos. Contiene todo el código de la aplicación, a excepción de las clases iniciadoras o "starter classes". El resto de los proyectos se vinculan a éste.
• El proyecto para escritorio: Tendrá el nombre que especificamos para el proyecto, con el sufijo "-desktop". Contiene la clase iniciadora para ejecutar la aplicación en el escritorio. Está vinculado a los recursos que contiene la carpeta assets/ en el proyecto de Android y al proyecto del núcleo.
Al querer desarrollar un juego que funcione en dispositivos con Android, no es necesario generar ninguno de los proyectos opcionales, ya que tanto el proyecto del core como el de Android son requeridos. Sin embargo, generar el proyecto para desktop es una buena idea para poder probar el juego en escritorio.
Si generamos estos tres proyectos tendremos:
Cada actor posee una lista de listeners que son notificados por eventos en ese actor. Los eventos son propagados en dos fases:
Fase de captura: Donde un evento es dado a cada actor, desde la raíz hasta el actor “target”. Esto le da a los actores padres una chance para interceptar y potencialmente cancelar los eventos antes de que el actor hijo lo pueda ver.
Fase normal: El evento es dado a cada actor desde el “target” hacia la raíz. Esto permite que los actores manejen un evento ellos mismos, o que lo haga el padre.
El evento provisto a cada actor cuando es notificado contiene su estado.
Cada actor posee una lista de acciones. Estas son actualizadas en cada cuadro mediante el método act(). Muchos tipos de acciones están incluidas en LibGDX. Estas pueden ser instanciadas, configuradas y añadidas a un actor. Cuando la acción está completada, automáticamente será removida del actor.
MoveToAction action = newMoveToAction();
action.setPosition(x, y);
action.setDuration(duration);
actor.addAction(action);
El método hit de un actor recibe un punto y retorna el actor más “profundo” en caso de colisión, o null si no hay colisión con ningún actor.
Cuando el método hit es llamado en el grupo del Stage, se llama al hit de cada hijo. El actor retornado en caso de colisión es el último hijo en la jerarquía que contiene ese punto.
Una vez descargado e instalado lo necesario:
3. Muestra una vista virtual del árbol de archivos que van a ser generados.
4. Se abre la pantalla de generación del proyecto
2. Se descargan las librerías necesarias, tanto requeridas como de terceros.
1. Se selecciona el nombre del proyecto, del paquete y de la clase del juego. Luego se indica la carpeta en donde se deben crear los proyectos y que proyectos se desean crear. (Siempre crea el proyecto del núcleo, y el de Android. Se puede decidir si crear los proyectos para desktop, html y iOS).
Los actores almacenan información que es considerada parte del modelo pero también son la vista, ya que saben cómo dibujarse. Esto hace que la separación MVC sea difícil. Cuando es utilizado sólo para interfaces de usuario o aplicaciones que no utilizan MVC, esto no es un problema.
Scene2D posee tres clases en su núcleo. La clase Actor, con una posición, medida rectangular, punto de origen, escala, rotación y color. La clase Group, que es un actor que puede tener otros actores hijos. La clase Stage posee una cámara, un SpriteBatch, y un grupo. Administra el dibujado de los actores y distribuye los eventos de entrada.
Cuando se llama al método draw del Stage, este llama al draw de todos sus actores. Lo mismo sucede cuando se llama a su método act.
El método draw de un actor dibuja una región utilizando la información del actor. El SpriteBatch que se le pasa para dibujar es configurado para dibujar en las coordenadas del padre, por lo que 0,0 es el borde inferior izquierdo del padre. Esto hace que dibujar sea simple, por más que el padre esté rotado y escalado.
Otros listeners también pueden manejar los eventos de entrada. Por ejemplo, ClickListener para eventos de mouse o touchScreen, o ActorGestureListener, que detecta presión larga, zoom, gestos como pinch, fling, entre otros.
Dentro de la carpeta de LibGDX descargada se encuentra un archivo llamado "gdx-setup-ui.jar"
Este jar permite crear los proyectos para luego importarlos a la IDE. Ejecutarlo:
Scene2D se utiliza para crear aplicaciones e interfaces de usuario utilizando una jerarquía de actores. Provee las siguientes funcionalidades:
La rotación y la escala de un grupo son aplicadas a todos los actores hijos.
Dibujo simplificado a través de SpriteBatch.
Detección de colisiones de actores. Cada uno determina si colisionó.
Ruteo de eventos de entrada u otros eventos al actor apropiado.
Sistema de acciones para la manipulación fácil de actores en el tiempo.
Scene2D está bien equipado para crear menús de juegos, HUDS, herramientas, y otras interfaces de usuario. El paquete scene2d.ui provee varios actores y otras utilidades específicamente para la creación de interfaces de usuario.
Al clickear Launch!, se generarán los proyectos en la carpeta especificada.
Universidad Tecnológica Nacional | Mar del Plata
Guía para importar los proyectos a Eclipse y para resolver el error "gwt-servlet not found" en los proyectos para html.
Las clases Widget y WidgetGroup extienden de Actor y Group respectivamente. Los widgets de interfaz de usuario deberán extender a WidgetGroup si poseen actores hijos. Si no, de Widget.
Los Widgets de UI poseen recursos configurables, como imágenes, fuentes, colores, etc. Los recursos necesarios para renderizar un widget son denominados un estilo. Cada widget posee su propio Style. El siguiente código fue utilizado para crear y darle estilo a uno de los botones del menú principal:
private TextButtonStyle estiloBotones;
private TextButton botonJugar;
...
estiloBotones = new TextButtonStyle();
estiloBotones.up = skin.getDrawable("botonNormal");
estiloBotones.down = skin.getDrawable("botonApretado");
estiloBotones.font = fuente;
...
botonJugar = new TextButton(" jugar ", estiloBotones);
Se le agrega al estilo una imagen para cada estado del botón. Luego se especifica la fuente del texto, y por último se crea el botón, donde se le pasa al constructor el texto y el estilo como parámetros.
Para la mayoría de tipos de eventos, se utilizan listeners específicos por conveniencia. Por ejemplo, InputListener se utiliza para recibir y manejar eventos de entrada. Un actor sólo necesita agregar un InputListener para comenzar a recibir eventos de entrada. Esta clase posee varios métodos que pueden ser sobreescritos:
botonJugar = new TextButton(" jugar ", estiloBotones);
botonJugar.addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {
juego.reproducirSonido(sonidoBoton);
juego.setScreen(new SeleccionNivel(juego));
return true;
};
});
En el ejemplo se le registra un InputListener a un TextButton (widget de Scene2d). En el listener se sobreescribe el método touchUp, evento propio del mouse o touchscreen. También se pueden sobreescribir otros métodos para estos dispositivos de entrada, como touchDragged, touchUp, enter, exit, mouseMoved, entre otros. También se pueden manejar eventos de teclado, como keyDown, keyUp, y keyTyped.
Para la creación de interfaces de usuario, el paquete scane2d.ui brinda widgets y otras clases creadas sobre scene2d.
Para desarrollar aplicaciones para Android utilizando LibGDX es necesario:
• Se recomienda utilizar Eclipse como entorno de desarrollo aunque se puede utilizar otro IDE. Este se puede obtener también de forma gratuita en la página de Eclipse:
http://www.eclipse.org/downloads/
• Descargar LibGDX desde la página de Bad Logic Games:
http://libgdx.badlogicgames.com/download.html
• Instalar el Java Development Kit (JDK) el cual se puede descargar desde la página de Oracle. La misma página posee instrucciones sobre la instalación:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
Una forma que representa una porción de una textura es denominada una región de textura (TextureRegion):
private TextureRegion regionMoneda;
regionMoneda =new TextureRegion(texturaMoneda,0,0,32,32);
En el ejemplo anterior, se crea una región de textura de 32x32 píxeles, partiendo desde el punto 0,0 de la textura.
La clase Sprite describe una región de textura, la geometría en la que será dibujada, y el color.
private Sprite sprite;
...
textura = new Texture(Gdx.files.internal("imagen.png"));
sprite = new Sprite(textura, 20, 20, 50, 50);
sprite.setPosition(10, 10);
sprite.setRotation(45);
...
batch.begin();
sprite.draw(batch);
batch.end();
Aquí, 20, 20, 50,50 describe la porción de textura. Luego es rotada 45 grados y es dibujada en la coordenada 10,10.
Una imagen que es subida al GPU (unidad de procesamiento de graficos) es denominada una textura (Texture). La imagen debe encontrarse en la carpeta “assets” del proyecto para Android. Las medidas de las imágenes deben ser potencias de dos, salvo que se trate de una aplicación que utiliza OpenGL 2.0.
private Texture texturaMoneda;
texturaMoneda = new Texture(Gdx.files.internal("moneda.png"));
public void renderizarObjetos() {
getSpriteBatch().enableBlending();
getSpriteBatch().begin();
renderizarMeta();
renderizarNinja();
renderizarPlataformas();
renderizarItems();
renderizarEnemigos();
renderizarShurikens();
getSpriteBatch().end();
}
Para dibujar texturas utilizando un SpriteBatch, se utiliza su método draw(). Posee varias sobrecargas para dibujar texturas y regiones de texturas.
Es común dibujar una textura mappeada a un rectángulo, y dibujar la misma textura o una región de la textura varias veces. Sería ineficiente mandar cada rectángulo al GPU para que sea dibujado. Por eso, muchos rectángulos para la misma textura pueden ser descriptos y mandados al GPU al mismo tiempo por medio de la clase SpriteBatch.
A este se le manda una textura y las coordenadas de cada rectángulo. Este recolecta la forma geométrica sin enviársela al GPU.
Todas las llamadas para dibujar con SpriteBatch deben ser realizadas entre sus métodos begin() y end() . Esto se puede ver en el siguiente fragmento de código del juego:
• Instalar el Android SDK (Software Development Kit), y el Eclipse ADT plugin (Android Development Tools plugin), los cuales se pueden descargar desde la página de Android Developers, donde también se encuentran las instrucciones para llevar a cabo las instalaciones:
http://developer.android.com/sdk/installing/index.html
Las animaciones en 2D se utilizan para crear la ilusión de movimiento utilizando imágenes estáticas.
Consiste de múltiples cuadros que son mostrados secuencialmente en ciertos intervalos.
Utilizar animaciones en LibGDX es muy simple, mediante la clase Animation.
Por medio del siguiente constructor es muy fácil crear una animación:
Animation (float frameDuration, TextureRegion... keyFrames);
Donde el primer parámetro es el tiempo del cuadro y el segundo son las regiones que formarán la animación:
Por ejemplo, la animación del Ninja saltando en el juego se creó a partir del siguiente código:
private Animation ninjaSaltando;
...
ninjaSaltando=new Animation(0.2f,
new TextureRegion((Texture)getAdminAssets().get("ninja.png"),0,11,32,53),
new TextureRegion((Texture)getAdminAssets().get("ninja.png"),32,11,32,53),
new TextureRegion((Texture)getAdminAssets().get("ninja.png"),64,11,32,53));
Uno de los métodos más útiles en la clase Graphics es getDeltaTime(), el cual provee el tiempo transcurrido desde el último cuadro renderizado. Esto puede ser útil para animaciones basadas en tiempo.
Otro método útil es getFramesPerSecond(), que retorna un promedio del rango de cuadros para propósitos de diagnóstico.
Para habilitarlo, y poder representar correctamente texturas con transparencias, se debe habilitar, como se puede ver en el siguiente código utilizado en el juego:
public void renderizarObjetos() {
getSpriteBatch().enableBlending();
getSpriteBatch().begin();
renderizarMeta();
renderizarNinja();
...
getSpriteBatch().end();
}
A veces es necesario saber en qué plataforma está ejecutándose la aplicación. El método Application.getType() retorna la plataforma en la que se está ejecutando. En este fragmento de código del juego se puede ver:
ApplicationType tipoApp= Application.getType();
if (tipoApp == ApplicationType.Android) {
mapa.update(deltaTime, Gdx.input.getAccelerometerX());
}
else {
float accel = 0;
if (Gdx.input.isKeyPressed(Keys.DPAD_LEFT))
acel = 5f;
acel = 5f;
if (Gdx.input.isKeyPressed(Keys.DPAD_RIGHT))
acel = -5f;
mapa.update(deltaTime, acel);
}
Un uso particular del módulo Graphics concierne al acceso más directo al contexto actual de OpenGL para consultas y comandos de nivel más bajo.
El siguiente fragmento de código del juego permite acceder al contexto OpenGL para setear el color del fondo de la pantalla a negro y limpiarla:
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Cada version de OpenGL ES está disponible a través de su propia interfaz al igual que la interfaz GLCommon, para comandos comunes a cualquier versión.
Cuando el blending está habilitado, las porciones traslucidas de una textura son combinadas con los otros píxeles. Si se deshabilita, los píxeles nuevos reemplazaran los anteriores. Esto es más eficiente, por lo que el blending debería siempre estar deshabilitado salvo que sea necesario.
En este fragmento de código se puede ver en la primer línea del método cómo deshabilitar el blending:
public void renderizarFondo() {
getSpriteBatch().disableBlending();
getSpriteBatch().begin();
//se llama al draw() del spritebatch...
getSpriteBatch().end();
}
Un ninepatch es una imagen que tiene definidas sus partes estirables. Como las áreas son pre-definidas, la imagen no se verá estirada. Para el manejo de estas imágenes, LibGDX utiliza la clase NinePatch.
Son utilizados principalmente en varios componentes de Scene2d, incluyendo Button, ScrollPane, Textfield, entre otros.
Manualmente, se puede instanciar un ninepatch de la siguiente manera:
NinePatch patch = new NinePatch(new Texture(Gdx.files.internal("textura.png")), 12, 12, 12, 12);
Los cuatro argumentos enteros especifican la región que se va a estirar.
También se puede crear el ninepatch automáticamente utilizando dos herramientas combinadas llamadas Draw 9-patch y TexturePacker.
private Texture textura;
private TextureRegion region;
private Sprite sprite;
...
textura = new Texture(Gdx.files.internal("imagen.png"));
region = new TextureRegion(texture, 20, 20, 50, 50);
sprite = new Sprite(texture, 20, 20, 50, 50);
sprite.setPosition(100, 10);
sprite.setColor(0, 0, 1, 1);
...
batch.begin();
batch.setColor(1, 0, 0, 1);
batch.draw(texture, 10, 10);
batch.setColor(0, 1, 0, 1);
batch.draw(region, 50, 10);
sprite.draw(batch);
batch.end();
Cuando se dibuja una textura, región o sprite, se le puede dar tinte. El siguiente código muestra como dibujar una textura, una región y un sprite con un color de tinte. Los colores son descriptos utilizando valores RGBA entre 1 y 0. El alfa es ignorado si se deshabilita el “blending.”
La clase Camera opera como una cámara simple del mundo real. Con ella es posible:
Utilizar la cámara es la forma más fácil para moverse en el mundo de un juego sin tener que operar manualmente las matrices. Todas las proyecciones y operaciones de matrices están escondidas en la implementación.
private OrthographicCamera camara;
camara = new OrthographicCamera(320, 480);
camara.position.set(320 / 2, 480 /2, 0);
El módulo Graphics provee información acerca del display del dispositivo actual y la ventana de la aplicación como información y acceso al contexto de OpenGL.
Como el resto de los módulos, se accede a través de los campos estáticos de la clase Gdx.
Se refiere al chequear repetitivamente el estado de un dispositivo de entrada. Es una forma rápida y fácil de procesar la entrada del usuario y es suficiente para la mayoría de juegos.
boolean pulsado_0 = Gdx.input.isTouched(0);
boolean pulsado_1 = Gdx.input.isTouched(1);
boolean pulsado_2 = Gdx.input.isTouched(2);
Para obtener las coordenadas de un puntero/dedo, se utilizan los siguientes métodos:
int primer_X = Gdx.input.getX();
int primer_Y = Gdx.input.getY();
int segundo_X = Gdx.input.getX(1);
int segundo_Y = Gdx.input.getY(1);
Existen una serie de métodos para hacer polling en la pantalla táctil y el mouse. Para consultar si el puntero/dedo está presionado puede hacer lo siguiente:
boolean pulsado = Gdx.input.isTouched();
Para consultar sobre entrada multi-touch, se le puede pasar como parámetro un número de dedo específico al método:
Los efectos de sonido son pequeños archivos de audio de unos pocos segundos, LibGDX soporta formatos MP3, OGG y WAV. Estos están representados por la interfaz Sound. Para cargar un efecto de sonido:
Sound sonido = Gdx.audio.newSound(Gdx.files.internal("miSonido.mp3"));
El código anterior carga un archivo de audio llamado “miSonido.mp3” de la carpeta assets.
El siguiente método reproduce el sonido una vez, con el volumen al máximo. Retorna un long con el id de la instancia de sonido que se está reproduciendo.
long id = sonido.play(1.0f);
El método stop detiene el sonido, recibiendo como parámetro el id de la instancia.
sonido.stop(id);
Sound posee métodos para el procesamiento del audio, como por ejemplo:
• setPitch(): cambia el tono del sonido.
• setPan(): cambia el paneo del sonido, siendo -1 la izquierda y 1 la derecha.
• setLooping(): hace que el sonido se reproduzca como un bucle.
Una vez que no se necesite, hay que asegurarse de desecharlo, y de no acceder a él una vez desechado ya que provocará errores:
sonido.dispose();
Para hacer polling en un teclado:
boolean flecha_izq_pulsada = Gdx.input.isKeyPressed(Keys.DPAD_LEFT);
El parámetro pasado al método es un código de tecla.
Este fragmento de código del juego modifica una variable si se presionan las flechas izquierda o derecha:
if (Gdx.input.isKeyPressed(Keys.DPAD_LEFT))
acel = 5f;
if (Gdx.input.isKeyPressed(Keys.DPAD_RIGHT))
acel = -5f;
Los principales dispositivos de entrada que soporta libGDX son el mouse, pantallas táctiles, y teclado:
LibGDX abstre de forma unificada el manejo del mouse y el touch, La única diferencia es que la entrada del mouse posee información adicional sobre el botón que fue presionado, y la entrada por medio del touch posee información del dedo que está presionando la pantalla.
Ambas formas de entrada pueden ser procesadas por medio de polling o manejo de eventos.
Un acelerómetro mide la aceleración de un dispositivo en tres ejes.
A partir de ella se puede derivar la inclinación u orientación del dispositivo.
La aceleración es medida en m/s². Si un eje está apuntando hacia el centro de la tierra, su aceleración será aproximadamente 10m/s². Si está apuntando hacia la dirección opuesta, la aceleración será de -10 m/s².
Las consultas sobre el acelerómetro solo pueden ser accedidas via polling en LibGDX:
floatacel_X = Gdx.input.getAccelerometerX();
float acel_Y = Gdx.input.getAccelerometerY();
floatacel_Z = Gdx.input.getAccelerometerZ();
El teclado genera eventos al apretar y soltar una tecla. Cada evento contiene un código de tecla que la identifica al ser apretada o soltada. Estos códigos pueden diferir en cada plataforma. LibGDX trata de esconder esto por medio de un código de teclas propio. Esto se puede ver en la clase Keys.
El manejo de eventos provee una forma de implementar interacciones con interfaces de usuario, donde las secuencias de entrada son importantes. Por ejemplo el ‘touch down’, ‘touch up’ de un botón significa que el usuario clickeó el botón. Estas interacciones son difíciles de implementar con polling.
Para consultar si un dispositivo de entrada específico se encuentra disponible en la plataforma donde la aplicación está ejecutándose, se puede utilizar el método Input.isPeripheralAvaliable():
boolean teclado_disponible= Gdx.input.isPeripheralAvailable(Peripheral.HardwareKeyboard);
boolean multitouch_disponible = Gdx.input.isPeripheralAvailable(Peripheral.MultitouchScreen);
multiplexer.addProcessor(new InputProcessorJuego());
Gdx.input.setInputProcessor(multiplexer);
El InputMultiplexer notificará sobre cualquier evento al primer InputProcessor que fue añadido a él. Si ese processor retorna false desde el método invocado para procesar el evento, esto indica que el evento no fue procesado y el multiplexer le notificará sobre el evento al próximo processor.
A veces se desea tener varios InputPorcessor. Por ejemplo, se puede tener un processor para la interfaz de usuario que debe ser invocado primero, y un segundo processor para eventos de entrada que manipulen el mundo del juego. Para ello se puede utilizar la clase InputMultiplexer:
InputMultiplexer multiplexer = new InputMultiplexer();
multiplexer.addProcessor(new InputProcessorUI());
Public class MainActivity extends AndroidApplication {
@Override
Public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
...
cfg.useCompass=false;
...
initialize(new Juego(), cfg);
}
}
La interfaz Application provee métodos para logging.
El mensaje puede ser informativo, de error, donde se puede mostrar opcionalmente una excepción, o de debug:
Gdx.app.log("Etiqueta", "Mensaje informativo");
Gdx.app.error("Etiqueta", "Mensaje de error", excepción);
Gdx.app.debug("Etiqueta", "Mensaje de debug");
Los mensajes son mostrados de forma diferente dependiendo de la plataforma. Por ejemplo, en desktop son mostrados en la consola, y en Android en LogCat.
Los siguientes tres métodos permiten “escuchar” los eventos del mouse/touch:
• touchdown(): Es llamado cuando un dedo toca la pantalla o un botón del mouse es presionado.
• touchDragged(): Es llamado cuando un dedo es deslizado sobre la pantalla, o el mouse es deslizado mientras un botón está siendo apretado.
• touchMoved(): Es llamado cuando el mouse es movido sobre la pantalla sin haber un botón apretado. Este evento es solamente relevante en desktop.
• Scrolled(): Es llamado cuando la rueda de scroll del mouse es girada. Nunca será llamado para dispositivos de pantalls táctiles.
Los siguientes tres métodos permitirán “escuchar” eventos del teclado:
• keyDown(): Es llamado cuando una tecla es presionada.
• keyUp(): Es llamado cuando una tecla es soltada.
• keyTyped(): Es llamado cuando un carácter Unicode fue generado por la entrada del teclado.
Una vez implementado el InputProcessor, se le tiene que avisar a LibGDX, así puede ser llamado cuando un evento de entrada ocurra:
Gdx.input.setInputProcessor(processor);
Desde este punto, todos los eventos de entrada serán procesados por la instancia de InputProcessor, antes de que se llame al ApplicationListener.render();
Cada uno de los métodosde InputPorcessor retorna un booleano. Se explicará por que a continuación.
Por defecto, estos dispositivos están habilitados. Deshabilitarlos cuando no es necesario su uso es una buena práctica ya que ahorra batería. Para deshabilitarlos se puede cambiar la configuración en la starter class del proyecto para esta plataforma antes que se llame al método initialize().
Este fragmento de código del juego realizado muestra cómo se deshabilita el compass en una simple línea:
El InputProcessor es utilizado en el juego, por ejemplo, en los botones del menú principal:
botonJugar.addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
juego.reproducirSonido(sonidoBoton);
juego.setScreen(new SeleccionNivel(juego));
return true;
};
});
El manejo de eventos se hace por medio del patrón observer. Primero se debe implementar una interfaz denominada InputProcessor.
Para cualquier sonido que sea más largo que varios segundos es preferible reproducirlo desde el disco que cargarlo completamente en la RAM. Para ello LibGDX provee la interfaz Music.
Para cargar una instancia de Music: Music musica = Gdx.audio.newMusic(Gdx.files.internal("musica.mp3"));
Para reproducir la música se utiliza el método play(): musica.play();
También posee otros métodos para el procesamiento de la música al igual que Sound:
musica.setVolume(0.5f);
musica.setLooping(true);
musica.stop();
musica.pause();
musica.play();
boolean isPlaying = musica.isPlaying();
boolean isLooping = musica.isLooping();
float position = musica.getPosition();
Las instancias de Music son pesadas, por lo que se deben desechar si dejan de ser necesitadas para liberar recursos:
musica.dispose();
Cuando un usuario presiona el botón back en Android, generalmente finaliza la actividad que se estaba ejecutando, pero existe la posibilidad de cambiarle el comportamiento:
Gdx.input.setCatchBackKey(true);
Al llamar a este método, el sistema operativo no cerrará la aplicación, y se le podrá dar un nuevo comportamiento por medio de un InputProcessor o Polling.
En este ejemplo del juego, se utiliza la tecla back en Android o Escape en desktop para volver a la pantalla de menú principal desde las pantallas de ayuda:
if (Gdx.input.isKeyPressed(Keys.BACK) || Gdx.input.isKeyPressed(Keys.ESCAPE)) {
juego.setScreen(new MenuPrincipal(juego));
}
Otra tecla a la que se le puede cambiar el comportamiento es la tecla de menú:
Gdx.input.setCatchMenuKey(true);
En desktop también se puede consultar cuáles son los botones que están siendo presionados:
boolean izquierdo_presionado = Gdx.input.isButtonPressed(Input.Buttons.LEFT);
boolean derecho_presionado = Gdx.input.isButtonPressed(Input.Buttons.RIGHT);
Permite escribir el código una vez y llevarlo a varias plataformas sin hacer modificaciones.
Una gran ventaja es que se pueden realizar iteraciones más rápidas al poder testear la aplicación en desktop.
Permite ir lo bajo nivel que uno desee, dando acceso directo a sistema de archivos, dispositivos de entrada, de audio, y a OpenGL a través una interfaz unificada de OpenGL ES 1.x y 2.0
LibGDX provee métodos para reproducir efectos de sonidos cortos, o música desde el disco. También provee acceso al hardware de audio.
El acceso a las facilidades de audio que brinda se hace por medio del módulo audio.
LibGDX pausa el audio cuando la aplicación está pausada y vuelve a reproducirlo cuando se regresa a la aplicación.
En desktop, los usuarios se pueden comunicar con la aplicación por medio del teclado y el mouse. En Android, el mouse es reemplazado por una pantalla táctil, y puede existir un teclado físico. Todos los dispositivos compatibles con Android también poseen acelerómetro y algunos un compass (sensor de campo magnético).
LibGDX abstrae todos esos dispositivos de entrada. El mouse y las pantallas táctiles son tratadas como si fueran lo mismo.
Para la entrada de datos por parte del usuario se puede hacer polling o registrar un listener que recibirá eventos de entrada en orden cronológico.
Todas las facilidades de entrada son accedidas por medio del módulo Input de LibGDX.
Ejemplo: El siguiente código es utilizado para ir de una pantalla a otra, si es que el usuario presiona la tecla Back en Android o Escape en Desktop:
if (Gdx.input.isKeyPressed(Keys.BACK) || Gdx.input.isKeyPressed(Keys.ESCAPE)) {
juego.setScreen(new MenuPrincipal(juego));
}
Es una librería gratuita para renderizar fuentes con alta calidad. Está escrita en C, diseñada para ser pequeña, eficiente, altamente modificable y portable.
Puede suceder que diferentes tipos de almacenamiento no estén disponibles dependiendo de la plataforma en la que la aplicación se está ejecutando. Se puede consultar vía el módulo Files.
En este ejemplo, se cargan los datos del juego si el almacenamiento externo está disponible:
Public void cargar() {
if (Gdx.files.isExternalStorageAvailable()) {
if (fh.exists()) {
perfil = json.fromJson(Perfil.class,Base64Coder.decodeString(fh.readString()));
}
}
}
La interfaz Application provee varios métodos para generar consultas, como por ejemplo obtener el tipo de la aplicación, el consumo de energía, etc.
LibGDX emplea librerías de terceros para proveer mucha de su funcionalidad:
Por ejemplo, para crear una textura a partir de un archivo que se encuentra en la carpeta assets (internal):
texturaFondo = new Texture(Gdx.files.internal("fondo_menu.png"));
En este otro ejemplo del juego se obtiene un FileHandle que representa un archivo externo donde están guardados los datos del juego:
private FileHandle fh;
...
fh = Gdx.files.external("ninjump.json");
Es un reproductor /decodificador de audio gratuito y de código abierto. Soporta formatos de audio MPEG, incluyendo MP2 y MP3.
Una vez obtenido un FileHandle, se puede leer el archivo a través de clases que saben cómo cargar su contenido, o lo puede leer uno mismo. Esto es realizado a partir de cualquier método de entrada en la clase FileHandle. Por ejemplo, para cargar texto de un archivo interno:
FileHandle fh = Gdx.files.internal("miArchivo.txt");
String text = fh.readString();
Si contiene datos binarios, se puede cargar el archivo dentro de un array de bytes:
FileHandle fh = Gdx.files.internal("miArchivo.bin");
byte[] bytes = fh.readBytes();
Esta clase posee muchos más métodos de lectura.
LibGDX es un framework multiplataforma para el desarrollo de juegos. Actualmente soporta las plataformas Windows, Linux, Mac OS X, Android, iOS y HTML5.
Existen cinco métodos en Gdx.files:
• Gdx.files.classpath(String): FileHandle
• Gdx.files.internal(String): FileHandle
• Gdx.files.local(String): FileHandle
• Gdx.files.external(String): FileHandle
• Gdx.files.absolute(String): FileHandle
(String: ruta del archivo)
Se pueden tener Fileandles de archivos que no existan. Si se intenta escribir sobre ellos, se crearán automáticamente. Si se intenta leer sobre ellos dará un error.
Es una API multiplataforma de audio. Está diseñada para renderizar audio multi-canal tridimensional. El estilo y convenciones de su API son muy parecidos a las de OpenGL.
A veces es necesario consultar sobre la existencia de un archivo específico o listar los contenidos de un directorio. La clase FileHandle provee métodos para poder llevar esto a cabo.
Por ejemplo, para consultar si un archivo existe se utiliza el método exists():
private FileHandle fh;
fh = Gdx.files.external("ninjump.json");
...
if (fh.exists()) {
perfil =json.fromJson(Perfil.class,Base64Coder.decodeString(fh.readString()));
}
...
Para consultar si se trata de un directorio o no:
boolean isDirectory = Gdx.files.external("ejemplo/").isDirectory();
Para listar un directorio:
FileHandle[] archivos = Gdx.files.local("miDirectorio/").list();
for (FileHandle file: files) {
// hacer algo
}
Un archivo en libGDX es representado por una instancia de la clase FileHandle. Esta posee un tipo, que define en donde se encuentra el archivo:
• Classpath: Almacenados en las carpetas fuentes. Son empaquetados con los jars y siempre son de solo lectura. Deben ser evitados si es posible.
• Internal: Se encuentran en la carpeta assets del proyecto de Android que está linkeada al resto de los proyectos. Sólo se pueden leer archivos.
• Local: Almacenamiento interno. En escritorio los archivos están juntos al JAR o en la carpeta del proyecto. En Android están en el almacenamiento interno del teléfono.
• External: Relativos a la raíz de la tarjeta SD en Android, y en la carpeta personal del usuario en desktop. Se recomienda su uso para datos volátiles, es decir, que no son imprescindibles para la aplicación.
• Absolute: Necesitan tener sus rutas completas especificadas. Por temas de portabilidad esta opción debe ser solo utilizada cuando sea absolutamente necesaria.
Es una librería destinada a la creación de juegos escritos en Java. Proporciona acceso a librerías multiplataforma como OpenGL y OpenAL para crear juegos de alta calidad con gráficos y sonido 3D. Sus funcionalidades están integradas en una sola API. Abstrae al programador de muchas dificultades, y proporciona un muy buen rendimiento.
Es un motor open source escrito en C++ que implementa un motor físico en dos dimensiones. Uno de los videojuegos que la emplea es el famoso “Angry Birds”.
Todo tipo de archivo puede ser copiado. El resto de las operaciones son posibles para tipos de archivos que se pueden escribir, es decir locales, externos y absolutos.
FileHandle fh = Gdx.files.internal("miArchivo.txt");
fh.copyTo(Gdx.files.external("miCopia.txt");
Gdx.files.external("miCopia.txt").rename("copiaExt.txt");
Gdx.files.external("copiaExt.txt").moveTo(Gdx.files.local("copiaLocal.txt"));
Gdx.files.local("copiaLocal.txt").delete();
Un FileHandle se obtiene directamente desde el módulo Files:
private FileHandle fh;
fh = Gdx.files.external("ninjump.json");
Las instancias de FileHandle son pasadas a métodos de clases que son responsables de leer y escribir información. Por ejemplo, Cuando se carga una imagen a través de la clase Texture, o cuando se carga audio vía el módulo Audio, un FileHandle tiene que ser especificado.
Es una especificación estándar que define una API multilenguaje y multiplataforma para escribir aplicaciones que produzcan gráficos 2D y 3D. Describe un conjunto de funciones y el comportamiento exacto que deben tener. Los fabricantes de hardware crean implementaciones que se ajustan a los requisitos de la especificación, que deben superar tests para calificar como conforme a OpenGL.
De forma similar a leer archivos, la clase FileHandle provee métodos para escribir en un archivo. Sólo se puede escribir en archivos locales, externos y absolutos.
Por ejemplo, para escribir una cadena de caracteres:
FileHandle fh = Gdx.files.local("miArchivo.txt");
fh.writeString("prueba escritura seminario", false);
El Segundo parámetro del método writeString especifica si el contenido debería agregarse al archivo. Si es false, el contenido será sobreescrito.
También se pueden escribir datos binarios en un archivo:
FileHandle fh = Gdx.files.local("miArchivo.bin");
fh.writeBytes(new byte[] { 20, 3, -2, 10 }, false);
Al igual que para la lectura, hay muchos otros métodos en FileHandle para la escritura de archivos.
Es una librería open source, escrita en c++ de procesamiento de audio, que permite cambiar el tempo, tonalidad, y el playback rate (tempo y tonalidad) del audio.
El modulo Files de libGDX permite:
• Leer un archivo.
• Escribir en un archivo.
• Copiar un archivo.
• Mover un archivo.
• Eliminar un archivo.
• Listar archivos y directorios.
• Averiguar si un archivo o directorio existe.
Cada una de las plataformas maneja el I/O de archivos de una forma diferente, pero gracias a este módulo el programador no tiene que preocuparse por ello.
LibGDX está compuesto por cuatro módulos, a los cuales se accede por medio de la clase estática Gdx:
• Input: Gestiona la entrada a través de teclado, pantalla táctil, acelerómetro, etc.
• Graphics: Permite gestionar la representación de imágenes en la pantalla.
• Files: Se trata de un componente para lectura y escritura de ficheros de datos como imágenes, archivos de configuración, sonidos, música, texturas,etc.
• Audio: Facilita la reproducción y grabación de audio en todas las plataformas que soporta.
Para cargar los assets primero se debe llamar al método load():
adminAssets.load("introduccion.png", Texture.class);
adminAssets.load("botones.atlas", TextureAtlas.class);
adminAssets.load("fuente_negra.fnt", BitmapFont.class);
adminAssets.load("botonMenu.mp3", Sound.class);
adminAssets.load("musicaMenu.mp3",Music.class);
Esas llamadas pondrán en una cola a los assets para ser cargados. Para cargarlos, se debe llamar al método update() continuamente:
(getAdminAssets devuelve la instancia de AssetManager).
public void render(float delta) {
if (juego.getAdminAssets().update()) {
((Game) Gdx.app.getApplicationListener()).setScreen(new MenuPrincipal(juego));
}
Mientras que el método update() del AssetManager retorne false, seguirá cargando assets. Para consultar el porcentaje de carga se puede utilizar el método getProgress().
La aplicación es el punto de entrada principal del proyecto. Setea una ventana y una superficie de renderización y maneja los distintos aspectos de la aplicación: Gráficos, audio, inputs y archivos.
Al haber creado los proyectos para Android y Desktop, tendremos dos implementaciones de esta interfaz:
AndroidApplication: Será instanciada en la "starter class" del proyecto para Android.
LwjglApplication: Será instanciada en la "starter class" del proyecto para Desktop.
Es importante mencionar que los assets administrados por el AssetManager no deberían desecharse manualmente, sino con el método unload().
Si se desean desechar todos de una vez, se puede llamar al método clear() o dispose() del AssetManager. La diferencia entre ellos es que el primero desecha los assets y remueve todos los assets en cola que no fueron cargados; mientras que el segundo también desecha el AssetManager.
Es recomendado utilizar un AssetManager para administrar los assets de la aplicación. Aunque si es muy simple y si la carga de recursos no tarda mucho tiempo no es necesario.
Las ventajas que brinda el uso de la clase AssetManager son las siguientes:
La carga de la mayoría de los recursos se hace de manera asíncrona, por lo que se puede mostrar una pantalla que reaccione a la carga de los assets.
Si existen por ejemplo dos assets A y B que dependen de un asset C, C no será desechado hasta que A, B, y C sean desechados. También si se carga un asset muchas veces, ocupará memoria sólo una vez.
Se tiene un solo lugar donde se pueden almacenar todos los assets.
Para crear un AssetManager es muy simple:
AssetManager adminAssets = new AssetManager();
En la implementación de la interfaz ApplicacionListener se le da comportamiento a los eventos del ciclo de vida (create(), resize(), render(), pause(), dispose() y resume()), y se le pasa una instancia de esa implementación a la Aplicación de cada una de las plataformas para las que se desee desarrollar el juego. La aplicación la llamará cada vez que un evento ocurra.
En esta imagen se puede ver la implementación de ApplicationListener que se crea por defecto al crear un proyecto:
Métodos del ciclo de vida de una aplicación libGDX:
• create(): Método llamado cuando la aplicación es creada.
• resize(int width, int height): Este método es llamado cada vez que la pantalla de juego es redimensionada y el juego no se encuentra pausado. También es llamado una vez luego del método create(): Los parámetros son las nuevas medidas de la pantalla redimensionada, en píxeles.
• render(): Método llamado por el bucle del juego cada vez que se deba renderizar. Las actualizaciones de la lógica del juego generalmente son también llamadas dentro de este método.
• pause(): En Android, este método es llamado cuando se presiona el botón Home, o se recibe una llamada. En desktop este método es llamado justo antes de que se llame a dispose() al salir de la aplicación.
• resume(): Este método es solo llamado en Android, cuando la aplicación vuelve del estado de pausa.
• dispose(): Método llamado cuando la aplicación es destruida. Es precedido por una llamada a pause().
Para utilizar los assets cargados en el AssetManager:
juego.getAdminAssets().get("ayuda1.png", Texture.class);
Esto es, asumiendo que los assets fueron cargados. Si se desea consultar si un asset se cargó se puede hacer lo siguiente:
If (juego.getAdminAssets().isLoaded("ayuda1.png"){
//hacer algo…
}
También es simple desechar los assets utilizando el AssetManager:
juego.getAdminAssets().unload("ayuda1.png");
El único código que necesita ser escrito para cada plataforma específica es el de las "starter classes". Para cada plataforma, una porción de código dentro de estas clases instanciará una implementación concreta de la interfaz "Application".
Al crear el proyecto con la GUI que nos brinda LibGDX, estas clases serán creadas automáticamente con código por defecto que puede ser modificado.
Si se crean los proyectos para Android y Desktop se contará con dos de estas clases:
En el método inicializador de cada starter class, se especifica una configuración, y se crea luego una instancia de la aplicación. Para ello se pasan dos parámetros
1. Una nueva instancia de la implementación de ApplicationListener que se encuentra en el proyecto del core. (En el proyecto se llama Juego.java).
2. La configuración especificada.
La aplicación luego llamará a los métodos del "ApplicationListener" en los momentos apropiados.
System.out.println(json.prettyPrint(perfil));
La salida se mostraría así:
{
"highscores": [
50
],
"estrellas": [
3
],
"ultimoNivel": 1,
"sonidoHabilitado": 1,
"musicaHabilitada": 1
}
Los nombres de las clases se muestran en la salida cuando son requeridos para la des-serialización
La clase JSON puede escribir y leer JSON, y formatos parecidos a él. Por ejemplo, JavaScript o minimal.
La clase Json serializa automáticamente objetos a JSON. Un ejemplo del juego:
public class Datos {
private Perfil perfil;
private Json json;
private FileHandle fh;
...
Public void guardar() {
if (Gdx.files.isExternalStorageAvailable()) {
String cadena = json.toJson(perfil);
fh.writeString(cadena, false);
}
}
...
Si se quisiera visualizar la salida de una forma legible se puede utilizar el método prettyPrint:
La clase Json se puede utilizar para des-serializar objetos desde JSON:
public void cargar() {
if (Gdx.files.isExternalStorageAvailable()) {
if (fh.exists()) {
perfil= json.fromJson(Perfil.class, fh);
}
}
}
El tipo pasado al método fromJson es el tipo de la raíz del grafo. A partir de ella, la clase Json determina los tipos de todos los campos y los otros objetos que encuentre recursivamente.
LibGDX puede realizar serialización de objeto a JSON y des-serialización de JSON a objeto. La API está conformada por cuatro clases:
• JsonWriter: Una API para crear JSON
• JsonReader: Parsea JSON y crea un DOM o Modelo en Objetos para la representación de Documentos.
• JsonValue: Describe un objeto, array, string, float, boolean o null JSON.
• Json: Escribe y lee utilizando JsonReader y JsonWriter.
ALCANCE
Este proyecto, orientado a la investigación, va a profundizar sobre las utilidades que brinda el framework LibGDX con lo que respecta al desarrollo de juegos para Android.
LIMITACIONES
Este framework también permite desarrollar juegos para Windows, Linux, iOS y HTML5, pero esta investigación se va a limitar a Android.
Además, con él se pueden hacer juegos en 3D, pero al estar en construcción el soporte de alto nivel en 3D, la investigación se limitará al desarrollo de juegos 2D.