Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Copy of Desarrollo de juegos para Android con LibGDX

No description
by

Joel Fernandez

on 13 July 2014

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Copy of Desarrollo de juegos para Android con LibGDX

DEFINICIÓN DEL PROBLEMA
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?

ALCANCE Y LIMITACIONES
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.

Desarrollo de juegos para Android con LibGDX
Cvitanovic, Ezequiel
Finkbeiner, Walter

Universidad Tecnológica Nacional | Mar del Plata
MARCO TEÓRICO
INTRODUCCIÓN A LIBGDX
¿QUÉ ES LIBGDX?
LibGDX es un
framework multiplataforma para el desarrollo de juegos
. Actualmente soporta las plataformas Windows, Linux, Mac OS X, Android, iOS y HTML5.
SOBRE LIBGDX
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



LIBRERÍAS DE TERCEROS
SOBRE LIBGDX
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.
LibGDX emplea
librerías de terceros
para proveer mucha de su funcionalidad:
BOX2D
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”.
SOUNDTOUCH AUDIO PROCESSING LIBRARY
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.
OPENAL
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.
LIGHWEIGHT JAVA GAME LIBRARY
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.
MPG123
Es un reproductor /decodificador de audio gratuito y de código abierto. Soporta formatos de audio MPEG, incluyendo MP2 y MP3.
FREETYPE
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.
OPENGL
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.
CREACIÓN DE UN PROYECTO DE JUEGO PARA ANDROID CON LIBGDX
PRE-REQUISITOS
Para desarrollar aplicaciones para Android utilizando LibGDX es necesario:
JDK
• 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
ECLIPSE
• 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/
ANDROID SDK
• 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
LIBGDX
• Descargar
LibGDX
desde la página de Bad Logic Games:

http://libgdx.badlogicgames.com/download.html
CREACIÓN
Una vez descargado e instalado lo necesario:
gdx-setup.ui.jar
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:
GUI - PANTALLA DE GENERACIÓN DEL PROYECTO
GUI - GUÍA PARA IMPORTAR LOS PROYECTOS A ECLIPSE
GUI PARA LA CREACIÓN DE PROYECTOS
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).
2. Se descargan las librerías necesarias, tanto requeridas como de terceros.
3. Muestra una vista virtual del árbol de archivos que van a ser generados.
4. Se abre la pantalla de generación del proyecto
Al clickear Launch!, se generarán los proyectos en la carpeta especificada.
Guía para importar los proyectos a Eclipse y para resolver el error "gwt-servlet not found" en los proyectos para html.
ESTRUCTURA DEL PROYECTO
PROYECTOS
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:
ESTRUCTURA
• 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 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 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.
APPLICATION, STARTERCLASS Y APPLICATIONLISTENER
STARTER CLASS
APPLICATION LISTENER
APPLICATION
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
.
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.

CICLO DE VIDA DE UNA APLICACIÓN LIBGDX
APPLICATIONLISTENER
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().

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ÓDULOS
SCENE2D
LEER Y ESCRIBIR JSON
ADMINISTRAR LOS ASSETS
CONSULTAS
INPUT
FILES
GRAPHICS
AUDIO
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.

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));
}
CONFIGURACIÓN Y CONSULTAS DE ENTRADA
MOUSE/TOUCH Y TECLADO
POLLING
BOTONES DEL MOUSE
MANEJO DE EVENTOS
ACELERÓMETRO
CATCHING DEL BOTÓN BACK Y MENU EN DISPOSITIVOS CON ANDROID
DESHABILITAR ACELERÓMETRO Y COMPASS
CONSULTAR SOBRE DISPOSITIVOS DE ENTRADA DISPONIBLES
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:

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);
}
}

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);


Los principales dispositivos de entrada que soporta libGDX son el mouse, pantallas táctiles, y teclado:
TECLADO
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.

MOUSE/TOUCH
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.

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.
POLLING EN EL TECLADO
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;


POLLING EN LA PANTALLA TÁCTIL Y MOUSE
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:
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);
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);


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.
INPUT PROCESSOR
El manejo de eventos se hace por medio del patrón observer. Primero se debe implementar una interfaz denominada InputProcessor.


INPUT MULTIPLEXER
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());

INPUT PROCESSOR - EVENTOS DEL TECLADO
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.
INPUT PROCESSOR - EVENTOS DEL MOUSE/TOUCH
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.


INPUT PROCESSOR - EJEMPLO
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;
};
});
SETEAR UN INPUT PROCESSOR
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.





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.


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();
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);
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.
CONTEXTO DE OPENGL
TIEMPO DE CUADROS
TEXTURE, TEXTUREREGION, SPRITEBATCH Y SPRITE
TINTE
BLENDING
ANIMACIONES EN 2D
OTRHOGRAPHIC CAMERA
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.

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.

TEXTURE
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.”
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();
}
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));

La clase
Camera
opera como una cámara simple del mundo real. Con ella es posible:
Mover
y
rotar
la cámara.
Hacer
zoom
Cambiar el
viewport
, es decir, el área visible, el cual puede ser parcial o total.
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);

NINEPATCHES
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.
SPRITEBATCH
SPRITE
TEXTURE REGION
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"));

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.

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:
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.
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.

TIPOS DE ARCHIVOS
CÓMO UTILIZAR ARCHIVOS
CONSULTA DE DISPONIBILIDAD DE ALMACENAMIENTO Y RUTAS
OBTENCIÓN DE FILEHANDLE
LISTADO Y CONSULTA DE PROPIEDADES DE LOS ARCHIVOS
LEER DESDE UN ARCHIVO
ESCRIBIR UN ARCHIVO
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.

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.
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.

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
}
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.

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.
ELIMINAR, COPIAR, RENOMBRAR Y MOVER ARCHIVOS/DIRECTORIOS
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();

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()));
}
}
}

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.

EFECTOS DE SONIDO
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();
MÚSICA
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();

INTRODUCCION A SCENE2D
DETECCIÓN DE COLISIONES
SISTEMAS DE EVENTOS
INPUTLISTENER
OTROS LISTENERS
ACCIONES
SCENE2D.UI
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.

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.
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.

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.
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);

Para la creación de interfaces de usuario, el paquete scane2d.ui brinda widgets y otras clases creadas sobre scene2d.
STYLE
LAYOUT
WIDGET Y WIDGETGROUP
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 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.
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.
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.
ESCRIBIR JSON
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
:
LEER JSON
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.

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.

ASSET MANAGER (1)
ASSET MANAGER (2)
ASSET MANAGER (3)
ASSET MANAGER (4)
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();

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()
.
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");
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.

OBTENER EL TIPO DE APLICACIÓN
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);
}
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.
LOGGING
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
.

MARCO TEÓRICO
CLASES GEOMÉTRICAS
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.

RECTANGLE
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
.
VECTORES
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.
ENCADENAMIENTO DE MÉTODOS
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);
}
IMPLEMENTACIÓN
IMPLEMENTACIÓN
BREVE EXPLICACIÓN DEL JUEGO (1)
ESTRUCTURA DEL PROYECTO
UTILIZACIÓN DE LIBGDX EN EL JUEGO
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.
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.

DIAGRAMA DE PAQUETES
DIAGRAMA DE CLASES
DIAGRAMA DE CLASES - PANTALLAS
DIAGRAMA DE CLASES - JUEGO
DIAGRAMA DE CLASES -ENTIDADES
DIAGRAMA DE CLASES -DATOS
DIAGRAMA DE CLASES -TWEEN
DIAGRAMA DE CLASES - SCENE2D
UTILIZACIÓN DE MATH
UTILIZACIÓN DEL MÓDULO INPUT
UTILIZACIÓN DEL MÓDULO GRAPHICS
UTILIZACIÓN GDX.APP
UTILIZACIÓN DEL MÓDULO FILES
UTILIZACIÓN DE SCENE2D
UTILIZACIÓN DE UTILS
UTILIZACIÓN DEL MÓDULO AUDIO
UTILIZACIÓN DE ASSETS
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.
CONCLUSIÓN
"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.

CONCLUSIÓN
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:
CONCLUSIÓN
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).

CONCLUSIÓN
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.


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;
}
}
}
BIBLIOGRAFÍA
• 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/


• 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/


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();
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();
}
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");
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.
INTRODUCCION A SCENE2D (1)
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.
INTRODUCCION A SCENE2D (2)
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.
BREVE EXPLICACIÓN DEL JUEGO
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.
CONCLUSIÓN
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;
};
}
);

CONCLUSIÓN
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.
Full transcript