Download - Programación de videojuegos con libGDX

Transcript

Programación de videojuegoscon

Introducción a libGDXDescargar la libreríaCrear un proyecto JAVA nuevoPortar el proyecto JAVA a un proyecto Android

Ciclo de vida de una aplicación libGDXGráficos

Texturas e InterpolaciónTextureRegion

TextuimagrePackerEmpaquetar imágenes automaticamenteCarga de imágenes empaquetadasDibujar imágenes en pantalla

EntradaDetectar pulsaciones en la pantalla táctil

AudioSounds

Cargar sonidosReproducir sonidosDescargar sonidos

MusicCargar una músicaDescargar una música

Navegando entre pantallas del juegoVibraciónCámarasAnimaciónManual de buenas prácticas

Separar la lógica de la presentaciónPáginas para Desarrollo de JuegosSolución de problemas

OBJETIVOEste documento pretende ser una guía de uso del Framework LibGDX para el desarrollo de videojuegosmultiplataforma. Para la redacción del mismo se va a recopilar la información de diversas fuentes como puede ser lapropia documentación de la API del framework, foros, blogs y videoblogs.

La idea es tener una documentación básica en castellano que esté viva, es decir, que se vaya actualizando con el pasodel tiempo e incluyendo las nuevas funcionalidades de las nuevas versiones.

INTRODUCCIÓNEn el mundo del desarrollo de aplicaciones actualmente existen multitud de Frameworks que ayudan al desarrolladora realizar sus proyectos minimizando el tiempo de desarrollo y simplificando tareas que, utilizando las herramientasusuales de cada plataforma, pueden llegar a ser complicadas o largas y tediosas.

Entre estos Frameworks, tenemos algunos que son propietarios en el que es preciso pagar una licencia para lacreación de proyectos comerciales como pueden ser Corona SDK, Unity 3D, Gideros (aunque en este caso, hay unaversión gratuita que se puede usar para proyectos comerciales con una intro mostrando el logo de Gideros), etc...Otros, por el contrario, siguen la filosofía del software libre y pueden ser usados para el desarrollo de software tantocomercial como privado sin el pago de licencia alguna como pueden ser Moai, Cocos 2D-X, libGDX...

Como se puede observar hay una gran cantidad de opciones donde elegir. En mi caso, he decidido dar el paso porlibGDX por varias razones: la comunidad es bastante activa, existe bastante documentación, usa Java como lenguajede programación que es el único que medio-domino, hace uso de herramientas contrastadas en el desarrollo devideojuegos como OpenGL, Open AL, Box-2D, etc...

LibGDX es un framework multiplataforma de desarrollo de juegos para Windows, Linux y Android. Está escrito enJava con una mezcla de C/C++ para dar soporte y rendimiento a tareas relacionadas con el uso de la física yprocesamiento de audio.

LibGDX permite generar una aplicación en su PC y utilizar el mismo código en Android, de esta manera el procesode pruebas y depuración se realiza de forma más rápida y cómoda ya que el PC es mucho más rápido que eldispositivo Android.

Con LibGDX nos aseguramos de que la misma aplicación puede funcionar correctamente en diferentes dispositivos.

LibGDX está compuesto por una serie de componentes que serán comunes a todas las aplicaciones:

● Marco de Aplicación (application): manejará el bucle principal y además estará encargado del clico devida, es decir, los eventos de creación,destrucción, pausa y resume de la misma.

● Componente de Gráficos (graphics): nos permitirá gestionar la representación de imágenes y objetosgráficos en la pantalla.

● Componente de Audio (audio): nos facilitará el acceso a los sonidos y música de la aplicación.

● Componente de entrada (input): para leer y escribir los diferentes ficheros de datos como por ejemplo, imágenes, archivos de configuración, sonidos, música, texturas,...

● Componente de Entrada/Salida (Files): gestionara la entrada a través del teclado, pantalla tácil o acelerómetro.

Los módulos descritos anteriormente pueden ser accedidos vía campos estáticos de la clase Gdx. Esto, en esenciason un juego de variables globales que permiten el fácil acceso a cualquier módulo de libgdx. Por ejemplo, paraacceder al módulo de audio, simplemente hay que escribir lo siguiente:

// Crea un objeto de tipo AudioDevice en el que poder cargar fragmenos deaudio de tipo PCM de 16 bitsAudioDevice audioDevice = Gdx.audio.newAudioDevice(44100, false);

Adicionalmente podríamos ampliar la gama de módulos con otros dos más.

● Math: permite la gestión rápida de cálculos matemáticos orientados al desarrollo de videojuegos.

● Physics: que básicamente es un wrapper de Box2D y permite controlar la gestión de colisiones.

Este gráfico muestra en mayor detalle el sistema de módulos de LibGDX

Realmente, estos módulos son sólo interfaces públicas que ofrece la librería.

La propia aplicación será la encargada de la gestión de los diferentes módulos.

Ventajas:● Soporte 2d full (bajo y alto nivel)● Mucha documentación, tutoriales, ejemplos de código● Releases en forma periódica● Se puede probar en escritorio (antes de subir a dispositivo móvil) ● Maneja Audio, eventos de entrada del usuario (Input), física,matemática,archivos● Soporta 3d aunque no es su fuerte.● libGDX te da un acceso más fácil a Bajo nivel● Posibilidad de tomar un juego hecho en java jar 2d o 3d y adaptarlo a libgdx para q funcione nativo en

android,eso hicieron con el juego Droid Invaders 3d.

Desventaja:● El soporte de alto nivel en 3d esta en construcción actualmente

Uso de la libreríaExisten dos métodos para el uso de la librería: el primero, más manual, que consiste en descargarla misma e importarla en nuestro proyecto de forma manual y el segundo método que es el másrápido y el que yo recomiendo que es usando un GUI creado en Java que crea un proyecto contodo lo necesario de forma automática para únicamente importarlo desde Eclipse.

Método 1: Manualmente

Descargar la libreríaPara descargar la librería debemos ir a la web de la misma: http://libgdx.badlogicgames.com/

Una vez en la web, pinchar en la opción Download & Source. A continuación seleccionamos ladistribución Nightly Builds que es la última versión que contiene las últimas actualizaciones (yalgunos bugs, también) pinchando en el botón Latest Nightly Build.

En la última ventana ya sólo queda pinchar en la última versión que se llama libgdx-nightly-latest.zip

Crear un proyecto nuevoAunque se puede programar con cualquier IDE que permita programar en Java (como Netbeans,InteliJ IDEA, etc... ), la mayoría de usuarios suele preferir usar Eclipse, por lo tanto es el IDEque se usará en este manual.

Para crear un nuevo proyecto usando LibGDX, lo más simple es usar una herramienta creada porAurelien Ribbon llamada LibGDX Project Setup. Simplemente hay que descargarlo del blog deAurelien Ribbon (http://www.aurelienribon.com/blog/). Una vez descargado, hay quedescomprimir el fichero y nos encontraremos con un programa realizado en Java, por lo tantosimplemente hay que ejecutarlo haciendo doble click en el fichero .jar.

Para crear un proyecto JAVA en Eclipse simplemente en el menú vamos a File > New > JavaProject. Le damos un nombre al proyecto (en este caso Demo1). Elegimos la ruta donde secreará en memoria y le damos a Finish.

Una vez creado el proyecto el siguiente paso será añadir la librería. Para ello una buena práctica es crear un directorio dentro del proyecto donde incluir las librerías que usemos.Para ello click derecho sobre nuestro proyecto y New > Folder. En nuestro ejemplo esta carpeta la llamaremos libs.

Una vez creada esta carpeta, simplemente tendremos que añadir a la misma lo que necesitamos para este proyecto. Para añadir, nos vamos al directorio donde hemos guardado la librería descargada en el apartado anterior y podemos arrastrar o copiar los archivos que necesitamos (en la imagen).Nos quedaría el proyecto de esta forma:

Ya tenemos las librerías en el directorio que hemos creado dentro del proyecto. Pero aún no forman parte del mismo,el siguiente paso es añadirlas. Para ello seleccionamos los cuatro ficheros dentro de la carpeta libs, hacemos clickderecho y en el menú contextual seleccionamos Build Path > Add to Build Path. El proyecto queda de la siguienteforma:

Otra buena práctica, aunque no necesaria es añadir la información contenida en el Javadoc de la librería. El objetivode esto es que cuando se muestre la ayuda emergente de eclipse veamos la información contenida en el Javadoc de lalibrería.

Para ello hacemos click derecho sobre el proyecto y en el menú contextual Build Path > Configure Build Path...tras lo que se nos muestra lo siguiente:

En esta ventana desplegamos gdx.jar y tal como se ve en la imagen seleccionamos Javadoc location. Una vez hechoesto pulsamos sobre Edit...

Pulsamos en Browse... y buscamos la localización del Javadoc que se encuentra en libgdx-nightly > docs > api.Una vez seleccionado este directorio pulsamos sobre Ok para confirmar el cambio. Y ya tenemos esta característicahabilitada.

Una vez preparado Eclipse y nuestro proyecto JAVA ya podemos empezar a trabajar.

Lo primero será crear la clase principal del juego, que será la encargada de manejar y controlar todo lo que ocurra enel juego.

Para ello click derecho sobre src > New > Class.

● Creamos la clase en el paquete com.desarrolladoresandroid.libgdx.demo1● El nombre de la misma es Demo1Game● Esta nueva clase heredará de Game (com.badlogic.gdx.Game).

Una vez creada la clase principal del proyecto, crearemos el punto de entrada para la ejecución de la aplicaciónJAVA. Una clase que nos permitirá lanzar el juego.

Para ello creamos una clase que contenga el método main:

● Nombre de la clase: Demo1Desktop● Mismo paquete que la clase principal● Seleccionamos la opción public static void main(String[] args)

Nos quedaría el proyecto de la siguiente forma:

Para poder lanzar el juego añadimos en la clase Demo1Desktop tenemos que añadir lo siguiente:

new LwjglApplication(new Demo1Game(), "Demo1", 320, 480, false);

Los parámetros indican lo siguiente:

● new Demo1Game(). Instancia de la clase que implementa Game● "Demo1". Que es el título de la aplicación.● 320. Ancho en píxeles.● 480. Alto en píxeles.● false. Para indicar que no queremos utilizar OpenGL S 2.0 en este caso. Por lo que se utilizará el 1.1

Una vez hecho esto ya tenemos un “juego” que no hace nada y que podemos lanzar como aplicación JAVA.

Para lanzarlo tenemos que hacer click derecho en el proyecto y Run As > Java ApplicationUna vez hecho esto nos saldrá una ventana para seleccionar el punto de entrada de la aplicación que en nuestroejemplo es Demo1Desktop.java

El resultado de la ejecución será una pantalla negra con la resolución indicada.

Portar el proyecto JAVA a un proyecto Android

Ahora crearemos el proyecto para compilarlo en Android. Para efectos practicos usare un ejemplo que yo ya tenía.Hecho en escritorio llamado HolaLibgdx.

Crear carpeta libs dentro del proyecto y ponerle las siguientes librerías de las que descargamos incluyendo esascarpetas:

Importante! "SE DEBEN INCLUIR LAS CARPETAS armeabi y armeabi-v7a como se ve en la imagen. Estascarpetas vienen en las librerías que descargamos"

Referenciamos las librerías copiadas. Click derecho al proyecto / build path / configure build path...

Antes de cerrar esa pantalla anterior ir a la pestaña project y adjuntarle la referencia al proyecto de escritorio.

Modificar la Activity principal "HolaLibGdxActivity" para que no extienda de Activity sino de AndroidApplication.(crt+shift+o) para resolver los imports necesarios.

package com.libgdx.holalibgdx; import android.os.Bundle; import com.badlogic.gdx.backends.android.AndroidApplication;import com.libgdx.holalibgx.HolaLibgdx; public class HolaLibGdxActivity extends AndroidApplication { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Aca inicializamos al app de escritorio. initialize(new HolaLibgdx(),false); }}

Debería quedarnos así.13. Lo ejecutamos en el emulador.

Desbloqueamos el dispositivo en el emulador.

y Se ejecuta exactamente lo mismo que en escritorio. Lo que hicimos recién, también compiló la aplicación en unarchivo .apk dentro del directorio /bin del proyecto android, ese archivo .apk se lleva al dispositivo fisico real y al ejecutarlo te instala la app creada.

Método 2: usando el libGDX Project SetupEste segundo método es mucho más sencillo y seguro que nos quita dolores de cabeza innecesarios ya quesimplemente ejecutando el GUI y rellenando unos pocos campos tendremos listo para importar a Eclipse no sólo unproyecto para escritorio, sino que también lo podrás generar para android, IOS y HTML5 de forma simultánea.

El único requisito es que para crear cualquier proyecto, es necesario crear obligatoriamente el proyecto paraAndroid. Lo que hace el asistente es crear un “Core” y un proyecto “Android”. Posteriormente el resto de proyectoshacen un soft-link a la carpeta Assests del proyecto Android, por lo que todos los recursos únicamente hay quealmacenarlos en dicha carpeta del proyecto de Android para que estén disponibles en el resto de proyectos.

Descargar el libGDX Project SetupSe descarga de la web de Aurelien Ribon pinchando aquí. Una vez descargado, simplemente se extrae del .zip elfichero .jar y se ejecuta. Aparecerá la siguiente pantalla:

Como se puede observar, se pueden realizar dos acciones: crear un nuevo proyecto (pinchando en “Create”) oactualizar un proyecto existente (pinchando en “Update”). Si pinchamos en “Create”, la siguiente pantalla será lasiguiente:

En esta pantalla tenemos cuatro partes diferenciadas por un número que sirven para lo siguiente:1. Configuration: en esta parte simplemente podremos poner el nombre del proyecto, el del paquete que lo va

a contener, el de la clase del juego, el destino, así como las plataformas que queremos generar (como sepuede comprobar, “core” y “Android” no se pueden deseleccionar porque son obligatorias). Si pinchamosen “Show advanced settings> “, nos dará la opción de escribir los sufijos que queremos que tengan losdiferentes proyectos. También nos permitirá configurar el SDK mínimo y el SDK objetivo de Android denuestra aplicación.

2. Library Selection: En esta parte está las diferentes librerías que queremos que tengan nuestros proyectos.Como mínimo es obligatorio que tenga LibGDX instalado (¡Obvio!). Para ello, si no la tenemosdescargada, nos permitirá descargarla pinchando en el icono de la derecha (nos descargará las “latestnightlies version”) o el penúltimo de la derecha (nos descargará la última versión estable). Tambiénpodremos descargar e incluir librerías de terceros.

3. Overview: Esta parte simplemente nos mostrará el árbol de ficheros que va a generar el asistente paranuestra información

4. Generation: Esta parte nos informa de si podemos pasar al proceso de generación o no y nos ofrecerá unbotón para ir a dicho proceso si todo está correcto.

Una vez pinchamos en el botón “Open de generation screen”, se nos mostrará la siguiente pantalla:

En esta pantalla, tenemos dos partes:● La parte de la derecha es otra vez la pantalla Overview que nos muestra el árbol de ficheros a generar.● La parte de la izquierda simplemente nos da la opción Launch! que generará nuestro proyecto y la opción

Back por si queremos hacer algún cambio antes de la generación.

Una vez se pincha en el botón de Launch! nuestro proyecto será generado en la ruta que le indicamos en pasosprevios. Finalmente se nos mostrará la siguiente pantalla con la información de cómo ha ido el proceso. Si no haocurrido ningún error, simplemente se cerrará para finalizar la ejecución del asistente.

Una vez hecho esto, sólamente queda importarlo desde Eclipse. Para ello, se abre Eclipse y se pincha en File ->Import, en la nueva ventana que aparece, seleccionar “Existing Projects into Workspace” y después al botónNext.

En la ventana que aparece a continuación, elegir la opción “Select root directory:” y pinchar el botón “Browse...”para que nos permita seleccionar la carpeta en la que se han creado todos nuestros proyectos desde el asistente delibGDX. Una vez seleccionamos la carpeta, en el cuadro “Projects:” nos aparecerán todos los proyectos que hay enel interior. Seleccionamos los que queramos importar (recordad que los proyectos core y Android son obligatorios),marcar la opción “Copy projects into Workspace” para que nos copie los proyectos importados en nuestra carpetade trabajo y a continuación pinchamos en “Finish”.

Con esto, tendremos los proyectos importados y con todas las librerías necesarias para comenzar a trabajar con libGDX. El arbol de ficheros que se crea en Eclipse debe ser similar al siguiente:

Ciclo de vida de unaaplicación libGDXEste apartado describe cómo se estructura una aplicación libgdx.

LA APLICACION

Es el principal punto de entrada para cualquier aplicación libgdx. La interface Application determina la plataforma ylos graficos de fondo que serán utilizados por la aplicación. La interface también proporciona los métodos paraacceder a gráficos, audio, archivos de entrada y módulos de E/S. También da acceso a un módulo de Logging elcual funciona en todas las plataformas.

Las clases de inicioEl único código de plataforma específica que es necesario escribir, son las llamadas clases de inicio. Por cadaplataforma, un pequeño trozo de código instanciará una implementación concreta de la interface Application. Parauna aplicación de escritorio, por ejemplo, usando el backend Lwjgl quedaría algo así:

public class DesktopStarter { public static void main(String[] argv) { LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); new LwjglApplication(new MyGame(), config); }}

Para Android, la clase de inicio correspondiente quedaría así:

public class AndroidStarter extends AndroidApplication { public void onCreate(Bundle bundle) { super.onCreate(bundle); AndroidApplicationConfiguration config = new AndroidApplicationConfiguration(); initialize(new MyGame(), config); }}

Estas dos clases viven en proyectos separados. El código actual de la aplicación está localizado en una clase queimplementa el interface ApplicationListener (MyGame en el ejemplo anterior). Una instancia de esta clase es pasadaa los métodos de inicialización respectivos de cada implementación de Application de cada backend. La aplicación

llamará entonces a los métodos de ApplicationListener en el momento adecuado tal como se comenta en el punto“Ciclo de vida de una aplicación” más adelante.

LibGDX es compatible actualmente con dos back-ends de aplicaciones de escritorio (lwjgl y JOGL), otra paraAndroid, otra para HTML5 y otra para IOS.

Para crear una aplicación libGDX, se ha de implementar la interfaz ApplicationListener primero.

APPLICATION LISTENER

ApplicationListener es el responsable de la inicialización de la aplicación, la actualización del estado del juego (esdecir, la lógica del juego), renderización de la imagen, pausado del juego, guardar el estado y la liberación de losrecursos al salir de la aplicación.

Es también el lugar donde los eventos del ciclo de vida son manejados. Cada aplicación / juego, sin importar elback-end y / o plataforma de destino tendrá que implementar la interfaz ApplicationListener. La implementación esexactamente la misma para todas las plataformas.

Por tanto, Application es responsable del circuito de juego y ApplicationListener es el lugar donde se implementala lógica del juego.

La implementación de la interfaz ApplicationListener

Las aplicaciones que usan libGDX, escucharán 6 notificaciones principales: CREATE, RESIZE, RENDER, PAUSE, RESUME y DISPOSE.

● create (): Se llama una vez cuando se crea la aplicación.

● resize(int width, int height): Se llama a este método cada vez que la pantalla del juego cambia sutamaño y el juego no está en el estado de pausa. También se le llama sólo una vez justo después delmétodo create (). Los parámetros son la nueva anchura y altura de la pantalla.

● render (): Método llamado por el bucle del juego de la aplicación cada vez que se renderiza. Laactualización del juego también tiene lugar aquí antes de la representación real.

● pause (): El método de pausa se llama justo antes que se destruya la aplicación. En Android sellama cuando el botón de inicio se presiona o haya una llamada entrante. En el escritorio se llama justoantes de dispose () al salir de la aplicación. Es un buen lugar para guardar el estado del juego en Android,ya que no se garantiza que sea reanudado.

● resume(): Este método es llamado sólo en Android, cuando la aplicación recibe el foco. En elescritorio este método no será llamado nunca.

● dispose (): Se le llama cuando la aplicación se destruye. Es precedido por una pausa ().

Ciclo de vida de una aplicación

Los métodos antes mencionados son provocados por la aplicación durante su ciclo de vida.

El siguiente diagrama muestra las diferentes fases por las que pasa una aplicación libGDX:

Módulos de libGDXEl núcleo de libGDX se basa en cinco interfaces que proveen formas de interactuar con el SO. Cada backendimplementa estos interfaces. Estos son:

● Aplicación (Application)● Gráficos (Graphics)● Entrada/Salida (Files)● Entrada (Input)● Audio (Audio)

Acceso a los módulosEl acceso a los módulos anteriormente listados puede hacerse a través de campos estáticos de la clase Gdx. Esencialmente es un juego de variables globales que permiten el fácil acceso a cualquier módulo de libGDX.

Por ejemplo, para acceder al módulo de audio simplemente se puede escribir lo siguiente:

// creates a new AudioDevice to which 16-bit PCM samples can be writtenAudioDevice audioDevice = Gdx.audio.newAudioDevice(44100, false);

Gdx.audio es una referencia a la implementación backend que ha sido instanciada en el inicio de la instanciaApplication.

GráficosEl módulo de gráficos es un resumen de la comunicación con el GPU y proporciona métodos de conveniencia paraobtener las instancias de OpenGL ES. Se encarga de todo el código repetitivo necesarios para hacerse con el ejemplode OpenGL y se ocupa de todas las implementaciones proporcionadas por el fabricante.Dependiendo del hardware subyacente, los contenedores pueden ser o pueden no estar disponible.El módulo de gráficos también proporciona métodos para generar mapas de pixels y texturas.En el siguiente código podemos ver cómo obtener una instancia de la API OpenGL 1.0:

GL10 gl = Gdx.graphics.getGL10 ();

El método devuelve una instancia que puede ser usada para dibujar en la pantalla. En el caso que la configuración dehardware no sea compatible con OpenGL ES 1.0, devuelve null.

Consideraciones previas sobre OpenGLPreviamente a dibujar nada con libGDX, lo primero es crear una cámara ortográfica. Esto es debido alfuncionamiento de OpenGL. Lo único que hay que tener en cuenta es que al crear una cámara ortográfica tienes quetratar el eje z a 90 grados como si estuvieras mirando un cuadro para poder ver en 2D.

Para crear una cámara ortográfica:

public class Game implements ApplicationListener { public OrthographicCamera camera;

public void create(){camera = new OrthographicCamera(640, 480);

}…

}

Otra cosa a tener en cuenta antes de renderizar imágenes en libGDX, es realizar un borrado de pantalla en el métodorender incluyendo las siguientes sentencias al principio:

gl.glClearColor(0.1f, 0.0f, 0.0f, 1); //configura un color y un alpha al //borrado

gl.glClear(GL10.GL_COLOR_BUFFER_BIT); //realiza el borrado con una máscara

Texturas e InterpolaciónTexture es una "imagen" en la memoria del chip gráfico (las imágenes en disco se cargarán en esta zona dememoria). En OpenGL v1.1 el ancho y alto de una textura tiene que ser potencia de 2 (...32, 64, 128, 256, 512, 1024,etc). Aunque en los ejemplos se definen la textura cuadrada, no tiene por qué ser así. Puedes tener una textura de128x32, 32x256, etc. . Si usamos OpenGL v2, no se tiene esta limitación.

Por ejemplo, el HTC G1 soporta como mucho, una textura de 1024x1024 (Aunque puedes tener más de una texturaa la vez). Intentar utilizar texturas más grandes en este dispositivo provocarán errores en el juego.La recomendaciónes que no pases del valor 1024 para establecer el ancho o alto de tus texturas.

Un parámetro muy importante que hay que indicar a la hora de crear un objeto Texture es un valor TextureOptionsque indicará qué método se utilizará para redimensionar las imágenes que tenemos dentro de la textura. (Para nocomplicar las cosas, dejaremos esa explicación).

Se pueden elegir los siguientes modos:

● NEAREST: La imagen aparecerá pixelada. Es mucho más rápido, pero la imagen tiene menor calidad.

Original:

Ampliada con NEAREST:

● BILINEAR: La imagen aparece más difuminada. Es un poco más lento, pero no verás píxeles en la misma.

Original:

Ampliada con BILINEAR:

● REPEATING: Si la textura es de 32x32 y tiene que dibujarse sobre una superficie de 64x64, veremos 4imágenes rellenando ese hueco. Como si pusiéramos la misma imagen una y otra vez repitiéndola. Estemodo no funciona con los Sprites normales, así que por ahora, vamos a ignorarlo.

Cuando utilizas imágenes que contienen valores alpha (transparencia) puedes encontrarte con que te aparezcan "halos" que rodean los bordes de las mismas. Aquí es donde entra en juego la técnica "premultiplied alpha", la cual corrige ese problema. Intenta evitar utilizar "premultiplied alpha" a menos que veas cosas raras en los bordes de tus imágenes, como sobras o halos resplandecientes que no deberían estar.

Si quieres una interpolación bilineal, estableces:

TextureOptions.BILINEAR

Si quieres una interpolación bilineal con "premultiplied alpha", estableces:

TextureOptions.BILINEAR_PREMULTIPLYALPHA

Lo mismo ocurre si quieres interpolación NEAREST con o sin "premultiplied alpha". Es cosa tuya explorar losvalores que puedes elegir de TextureOptions.

Para finalizar, decir que:

TextureOptions.DEFAULT = TextureOptions.NEAREST_PREMULTIPLYALPHA

TextureRegion

Define un área rectangular de una textura. El sistema de coordenadas utilizado tiene su origen en la esquina superiorizquierda con el eje x apuntando hacia la derecha y el eje y apuntando hacia abajo.Las textureRegion son empleadas para crear nuevas imagenes( o también llamados actores de tipo imagen)asociadas a un área de una textura.

Tiene varias opciones para construir un objeto de esta clase, son los siguientes:

TextureRegion() Construye una region sin textuta ni coordenadas definidas.Se podran definir despues empleando los métodos de la clase.

TextureRegion(Texture texture) Construye una region de tamaño especificado en la textura.

TextureRegion(Texture texture, float u, float v, float u2, float v2)

TextureRegion(Texture texture, int width, int height) Construye una región con con las coordenadas de la textura y el ancho y alto especificado.

TextureRegion(Texture texture, int x, int y, int width, int height) Define una tetura especificando el punto (x,y) de origen y el tamaño del area (ancho,alto)

TextureRegion(TextureRegion region) Construye una región con la misma textura y coordenadas que la región pasada como parámetro.

TextureRegion(TextureRegion region, int x, int y, int width, int height) Construye una región con la misma textura y coordenadas que la región pasada como parámetro.

Un ejemplo de selección de un área con TextureRegion es el siguiente:

TextureRegion texture = new TextureRegion(new Texture(

Gdx.files.internal("data/jugador.png")), 0, 0, 40, 40);

TexturePacker

Empaqueta imagenes en un Texture Atlas.

Lo ideal es almacenar muchas imágenes más pequeñas en una imagen más grande,obligar a la textura más una vez,luego dibujar porciones de la misma muchas veces. libgdx tiene una clase TexturePacker que es una aplicación delínea de comandos queempaqueta muchas imágenes más pequeñas en 1imágen más grande.

TexturePacker se basa en este algoritmo. También tiene una fuerza bruta, el embalaje de varias maneras y luegoelegir el resultado más eficiente.

Empaquetar imágenes automaticamente

Se deberá importar la libreria: gdx-tools.jar

El código para ejecutar el TexturePacker:

Settings settings = new Settings();settings.padding=2;settings.maxHeight=1024;settings.maxWidth=1024;settings.incremental=true;

TexturePacker.process(settings, "images", "data");

Se deberá tener en el proyecto una carpeta images, con las imagenes a procesar. El proceso generará la carpeta datacon el pack realizado.

Carga de imágenes empaquetadasAsi se recuperan las texturas de un textureAtlas Generado por un packer.

public static AtlasRegion background;atlas = new TextureAtlas(Gdx.files.internal("data/pack"));background = atlas.findRegion("background");

Dibujar imágenes en pantalla

Creamos la clase Asset para almacenar los recursos de imagenes.

public class Assets {

/** Contiene el paquete de texturas. */public static TextureAtlas atlas;

/** Regiones dentro de la imagen de textura empaquetada */public static AtlasRegion background;public static AtlasRegion soundoff;public static AtlasRegion soundon;public static AtlasRegion start;public static AtlasRegion title;

/** * Load. */public static void load(){

atlas = new TextureAtlas(Gdx.files.internal("data/pack"));background = atlas.findRegion("background");soundoff = atlas.findRegion("soundoff");soundon = atlas.findRegion("soundon");start = atlas.findRegion("start");title = atlas.findRegion("title");

}

public static void dispose(){atlas.dispose();

}}

public class HolaLibgdxExample implements ApplicationListener {

/** The gui cam. */OrthographicCamera guiCam;

/** Se utiliza para dibujar y optimizar las imagenes en el renderizadode la pantalla. */

SpriteBatch batcher;

@Overridepublic void create() {}

@Overridepublic void dispose() {batcher.dispose();}

@Overridepublic void pause() {

}

@Overridepublic void render() {

GL10 gl = Gdx.graphics.getGL10(); //referencia a OpenGL 1.0gl.glClearColor(0,1,0,1);gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

guiCam = new OrthographicCamera(10, 15);

//definicion de nuestra propia medida del juegoguiCam.position.set(10f / 2, 15f / 2, 0);

// Donde estara mirando la camarabatcher = new SpriteBatch();

//crear solamente un batcher por pantalla y eliminarlo cuando no se useguiCam.update();batcher.setProjectionMatrix(guiCam.combined);

//Dibujando el Backgroundbatcher.disableBlending();

//Se elimina graficamente la transparencia ya que es un fondobatcher.begin();batcher.draw(Assets.background, 0, 0, 10, 15);batcher.end();

//Dibujando elementos en pantalla activamos el Blendingbatcher.enableBlending();batcher.begin();batcher.draw(Assets.title, 1, 8, 8, 6);batcher.draw(Assets.start, 2, 5, 6, 1);batcher.draw(Assets.soundon, 2, 3, 6, 1);batcher.end();

}

@Override

public void resize(int arg0, int arg1) {}

@Overridepublic void resume() {}

}

Entrada

El módulo de entrada permite capturar los diversas formas de entrada de cada plataforma. Permite saber del estadode cada tecla, la pantalla táctil y el acelerómetro.

En el escritorio la pantalla táctil se sustituye por el ratón, mientras que el acelerómetro no está disponible.

En el fragmento de código siguiente se obtiene el touch actual de coordenadas si hay un touch del celular (o delratón en el escritorio):

if (Gdx.input.isTouched ()) { System.out.println ("de entrada se produjo en x =" + x + ", y =" + y);}De manera similar a todos los medios de entrada pueden ser consultados y manipulados.

Detectar pulsaciones en la pantalla táctil

Se debe difinir un Vector, un area ficticia donde se captará el evento de pulsación del dedo.Usamos tambien el objeto de OrthographicCamera, para convertir los pixeles en nuestra medida en metros. Luegopreguntamos si el boton que creamos “BoundingBox” tiene un touch.

Y escribimos el código para manejar ese evento

Audio

LibGDX cuenta con un módulo de audio que ofrece dos tipos de recursos: El sonido y la música.Ambos tipos soportan el formato WAV,MP3 y OGG. También ofrece acceso al hardware de sonido.

La diferencia entre el sonido y la música es que el sonido se almacena en memoria y se usa cada vez que se quierereproducir alguno de ellos, mientras que la música son streams de sonido que están almacenados en ficheros y a losque se accede cada vez que se quiere reproducir algo.

El siguiente fragmento de código reproduce un fichero de música desde el disco con el volumen a la mitad:

Music music = Gdx.audio.newMusic(Gdx.files.getFileHandle("data/myMusicFile.mp3", FileType.Internal));music.setVolume(0.5f);music.play();music.setLooping(true);

SonidosSound es una clase destinada a la reproducción de un efecto de sonido. Lo normal es cargar el sonido desde un

archivo a memoria y luego reproducirlo a través del método Sound.play(). Los sonidos son generalmente cortos,llegando a durar unos pocos segundos ya que al ser cargados en memoria pueden llegar a ocupar demasiado,incidiendo directamente en el rendimiento.

Cuando ya no vayamos a usar más ese sonido será necesario llamar al método Sound.dispose() para liberarmemoria en el dispositivo.

Cargar sonidos

Los Archivos para los efectos de sonido los pondremos dentro de una carpeta llamada data

Para cargar en memoria un efecto de sonido tan sólo será necesario definirlo como tipo Sound y llamar al métodoGdx.audio.newSound pasando como parámetro el fichero dentro del directorio data.

El siguiente fragmento de código carga un sonido en memoria.

Sound explosionexplosion = Gdx.audio.newSound(Gdx.files.internal("data/explosion.ogg"));

Puesto que el sonido será una cosa que sólo se cargará una vez durante toda la vida de la aplicación, haremos lallamada a newSound en nuestro método destinado a la carga de datos.

Reproducir sonidosPara reproducir un sonido previamente cargado en memoria tan sólo será necesario hacer una llamada al métodoplay del sonido pasando como parámetro un valor entre 0 y 1 que representará el volumen.

explosion.play(1);

Descargar sonidosUna vez que no queramos hacer uso del sonido será necesario descargarlo de memoria ya que en los dispositivosmóviles, la memoria RAM sigue siendo un elemento muy precidado.

Para descargar de memoria un sonido tan sólo será necesario llamar al método dispose del objeto. Esto podemoshacerlo en nuestro apartado destinado a la descarga de datos o bien en el método dispose de la aplicación, como yahemos visto en el apartado de ciclo de vida de la aplicación.

explosion.dispose();

MusicaMusic es la clase de libgdx para cargar archivos de sonido en nuestros juegos deriva de la clase Disposable consta delos siguientes métodos:

● play() →Comienza a la canción,en el el caso de estar pausada la reanuda donde se quedo y en el

caso de que se hubiera parado la comienza desde el principio.● pause() →Pausa la canción, si la canción no ha empezado aun o esta ya ha finalizado, este método

es ignorado al llamarlo.● stop() →Para la canción.La siguiente vez que se llame al método play() la canción comenzará

desde el principio.● isPlaying() →Función que devuelve un booleano, indicando si es verdad o no que la canción esta

sonando.● setLopping() →Admite como parámetro un booleano para indicar si se activa la función loop en

la canción, esta función activa la repetición indefinida de la música, de tal forma que si se llama alprocedimiento indicando true, la canción se repite indefinidamente hasta que la paremos o hasta quedesactivemos el loop.

● isLopping() →Función que devuelve un booleano, indicando si es verdad o no que la canción estasonando.

● setVolume() →Varía el volumen del sonido, admite por parámetros (0 ó 1) ,con un 0 se pone ensilencio y con 1 se pone el volumen al máximo.

● getPosition() → Función que devuelve un float con la posición de la canción en milisegundos● dispose() → Es el destructor de la clase. Es muy conveniente llamarlo cuando no sea necesaria la

musica ya que consume memoria del disposivo-

Libgdx soporta los formatos MP3, OGG y WAV. Es conveniente usar Ogg en casi todos los casos.Si necesita más control sobre el dispositivo de audio, puede utilizar el AudioDevice y clases Audiorecorder quetambién se obtiene de la interfaz de audio. Estas clases permiten a la salida de las muestras de PCM para eldispositivo físico de audio, así como muestras PCM registro de un micrófono conectado.

Cargar una músicaLas músicas se pueden almacenar de forma interna en el proyecto que estamos desarrollando o en una ruta fija(porejemplo para poner como música un sonido standard del sistema)

Para cargar una música en memoria debemos instanciar un objeto de la clase Music pasandole como parámetro unpuntero al fichero del tipo FileHandle nativo de java, de la siguiente forma:

Music music = Gdx.audio.newMusic(Gdx.files.getFileHandle("data/8.12.mp3", FileType.Internal));

En este ejemplo la canción 8.12.mp3 esta guardada dentro de la carpeta del proyecto data, por lo que el tipo dearchivo le indicamos que es interno. FileType.Internal. En el caso de no poder abrir el archivo se genera unaexcepción GdxRuntimeExceptionLos tipos posibles de ficheros son los nativos de Javade tipo File.FileType, estos son:

● External → Path relativo a la raiz del almacenamiento SD de android.● Internal → Path relativo a la ruta de la aplicacion android o dicho de otro modo relativo al

directorio raiz de la aplicación creada.● Classpath → El path del fichero esta en una ruta relativa a la raiz del Clashpath ● Absolute → El path esta indicado de forma absoluta, indicando el filesystem que lo contiene.

Reproducir una música

Para reproducir una música como hemos mencionado antes basta con llamar al metodo play() desde el objetoinstanciado de tipo Music.Vamos a verlo mas claramente con un pequeño ejemplo de como dejar una música reproduciéndose en un bucleinfinitodonde la música es un archivo mp3 almacenado en una carpeta que hemos llamado data

Music music = Gdx.audio.newMusic(Gdx.files.getFileHandle("data/8.12.mp3", FileType.Internal));music.setLooping(true);music.play();

Descargar una músicaUna vez que no queramos hacer uso del sonido será necesario descargarlo de memoria.Para descargar de memoria un sonido tan sólo será necesario llamar al método dispose del objeto. Esto podemoshacerlo en nuestro apartado destinado a la descarga de datos o bien en el método dispose de la aplicación, como yahemos visto en el apartado de ciclo de vida de la aplicación.En el caso de que la música estuviera en un bucle lo lógico es pararla antes de eliminarla por seguir una secuencialógica, y en parte por estética de la programación, estos se hace llamando al método stop, por tanto el orden seria :

music.stop();music.dispose();

Navegando entre pantallas del juego

Game extiende de AplicationListener para implementar multiples pantallas.Una Screen es una pantalla de un juego. Opciones, Menu principal, nivel 1, nivel 2, creditos, etc.

Para cambiar entre pantallas haremos:

Screen gameScreen = new GameScreen(game);game.setScreen(gameScreen);return;

es decir, creamos la screen, y se la seteamos a la instancia del juego.

VibraciónPara agregarle al juego del arkanoid (el del video tutorial) que vibre al chocar la barrita con la pared izquierda pormodificamos el método render() de la clase GameScreen.

try { Gdx.input.vibrate(200); System.out.println("vibra"); } catch (Exception e) {

System.out.println(e);}

Cámaras

OrthographicCamera guiCam;guiCam = new OrthographicCamera(10, 15); //definicion de nuestra propia medida del juego

10x15 metros: Esto es porque dividmos los 320x480 pixeles en bloques de 32px.

guiCam.position.set(10f / 2, 15f / 2, 0); // Donde estara mirando la camara

AnimaciónCrearemos un personaje de un personaje y haremos una animación, es decir, que camine como si se estuvieramoviendo.

Aquí se ve el ejemplo de un hombre corriendo. La imagen se puede ver como una matriz de imágenes las cuales sevan a recorrer con dos ciclos y se van a pasar por imagen a un vector.Cuando se crea la animation, se le pasa por parametro la velocidad con la que se mueve el frame y el vector deimágenes.Por último cuando se invoca al spriteBatch.draw(); se le indica el vector que contiene las imágenes, la posición de Xy de Y en donde se quieren dibujar las mismas.

Usaremos la siguiente Imagen como secuencia:

Copiaremos esta imagen, en la carpeta /imagen del proyecto

Creamos una clase que implemente screen animación:

public class AnimationScreen implements Screen {Game game;

static final float WORLD_WIDTH = 5;static final float WORLD_HEIGHT = 3.3f;private static final int FRAME_COLS = 6; // #1private static final int FRAME_ROWS = 5; // #2OrthographicCamera guiCam;SpriteBatch batcher;

Animation walkAnimation; // #3Texture walkSheet; // #4TextureRegion[] walkFrames; // #5SpriteBatch spriteBatch; // #6TextureRegion currentFrame; // #7

float positionX=1;

float stateTime; // #8}

Inicializamos en el constructor lo referente a la animación:

public AnimationScreen(Game game) {this.game = game;

batcher = new SpriteBatch();guiCam = new OrthographicCamera(WORLD_WIDTH, WORLD_HEIGHT);guiCam.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2, 0);

walkSheet = new Texture(Gdx.files.internal("images/man.png")); // #9TextureRegion[][] tmp = TextureRegion.split(walkSheet,

walkSheet.getWidth() / FRAME_COLS, walkSheet.getHeight() / FRAME_ROWS); // #10

walkFrames = new TextureRegion[FRAME_COLS * FRAME_ROWS];

int index = 0;for (int i = 0; i < FRAME_ROWS; i++) {

for (int j = 0; j < FRAME_COLS; j++) {walkFrames[index++] = tmp[i][j];

}}

walkAnimation = new Animation(0.025f, walkFrames); // #11spriteBatch = new SpriteBatch(); // #12stateTime = 0f; // #13

}

Ahora en el render hacemos:

Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // #14

stateTime += Gdx.graphics.getDeltaTime(); // #15

currentFrame = walkAnimation.getKeyFrame(stateTime, true); // #16spriteBatch.begin();

spriteBatch.draw(currentFrame, positionX, 2); // #17spriteBatch.end();

update(delta);

#16: obtenemos el frame correspondiente en base al tiempo que lleva moviendose

Metodo Update

public void update(float delta){positionX+=0.1f;

}

Si necesitan el código fuente completo de este ejemplo: [email protected]

Manual de buenasprácticas

TO DO: Introducción a cosas para ser más eficientes y limpios escribiendo código.

Para ser limpios y eficientes escribiendo código es importante tener en cuenta varias cosas como creaciones de métodos para simplificar el código, no hacer condiciones muy largas o muchos bucles anidados, estos son solo algunos ejemplos, ahora explicare un poco mas extenso algunos de los puntos mencionados anteriormente.

- Definir variables al principio de la clase/método, de esta forma se tiene un acceso rápido a esa variable y sabemos de un simple vistazo que atributos tiene.

- Respetar las reglas de nomenclatura de los lenguajes tales como los siguientes:- Los nombres de las variables comienzan con letras minúsculas, y en caso de contener

varias palabras se le pondrá mayúscula a la primera letra de cada palabra a partir de la segunda palabra.

- Los nombres de los métodos se escribirán en minúscula siguiendo la misma regla que el nombrado de las variables. Por lo general, el nombre de estos suelen ser verbos.

- Las variables finales se escribirán integramente en mayúscula, separando, si fuera necesario, diferentes palabras por medio del carácter guión bajo “_”

- El nombre de los paquetes se realizará integramente en minúscula, aunque contenga varias palabras.

- El nombre de las clases comenzará con la primera letra en mayúscula y las siguientes en minúscula. En caso de contener varias palabras se las distingirá poniendo mayúscula la primera letra de cada palabra.

- Tabular el código para una mayor compresión del mismo. En java, a diferencia de otros lenguajes de programación como puede ser python, no es necesario que las lineas de código estén tabuladas para pertenecer a un método/función/clase ya que para esto se utilizan las llaves, pero de esta forma se hace el código más legible.

- Realizar un estudio que relacione el tiempo de procesamiento vs almacenamiento. En ocasiones

una lista puede estar muy bien para almacenar datos, pero un array tiene un acceso inmediato al dato contenido.

- Si ves que programando escribes varias veces lo mismo, creaciones de objeto que varían muy poco, comprobación de un objeto, cambiar un objeto, lo mas probable es que puedas cambiarlo por una función o un método. Por ejemplo:

- Crear un casillero de 10 x 10, esto implica dibujar o crear 100 casillas, escribirías 100 veces:batcher.draw(casilla, posX, posY, 1, 1);

Y esto no es eficiente, pero podrías dibujarlo con:

for (int i = 1; i < 11 ; i++) {for (int j = 1; j < 11; j++) {

batcher.draw(casilla, i, j, 1, 1); }}

}

- Usar switch case si ves que comparas lo mismo dentro de muchos if, teniendo en cuenta que un case solo comprueba integer y enumerados.

- Antes de crear un método comprobar que no existe ya, o parecido, para que puedas modificarlo, siempre que al modificarlo no haya nada dependiendo de el.

- Usar varios for anidados hace que el programa se ralentice mucho, ya que podemos encontrarnos con recorridos con muchos elementos y los recorre todos por lo que ralentiza mucho y no es eficiente, esto no suele aparecer mucho pero hay que tener cuidado a la hora de meter for while o do, uno dentro de otros ya que puede dar problemas

Otra de las cosas que hay que tener en cuenta a la hora de programar es ser limpio y no escribir como se nos van ocurriendo las cosas y dejarlo así, ya que si después alguien quiere usar nuestro o código o simplemente saber como funciona le sera imposible, por eso cuando estemos programando hay que intentar dejar lineas simples, o explicar con comentarios cuando veamos que algo puede que no se entienda.Lo que también ayudara a la hora de la limpieza y de la claridad seran la clases, lo mas importante en el caso de la programación orientada a objetos. Siempre que vallas a utilizar un objeto que vaya a tener mas de una propiedad se utilizan clases. Algunos ejemplos:

- Pelota en un espacio: Además de su nombre tendrá un x e y indicando su posición.- Personaje: Además de su nombre puede tener su posición indicada con x e y, un

inventario guardado en un array, o cualquier cosa.En estos y en muchos mas la creación de una clase es lo mejor, cuando veas que estas creando dos variables para guardar información sobre un objeto dentro de una clase seguramente podrás

instanciar ese objeto desde otra clase y sera mas claro que poniendo tantos atributos.

Separar la lógica de la presentación

Con lógica nos referimos a todas las comprobaciones, modificaciones o creaciones que se hacen antes de renderizar, ya que el proceso de renderizado solo ya es lento si metes también toda la logica ara que el juego sea lento, por esto es recomendable hacerlo en una clase aparte.

Para hacer la parte lógica basta con crear un clase aparte del renderizado donde comprobemos lo que haga falta antes de pintar en pantalla. Por ejemplo:-Cambiar la velocidad de una pelota al chocar con una pared:

// Choque en pared izquierdaif(ball.position.x - 0.25 < 1){

ball.position.x = 1 + 0.25f;ball.direction.x = ball.VELOCITY;

}

-Comprobar el valor de una casilla para pintarla de un color diferente:

public AtlasRegion casilla(int x, int y){switch(matriz[x][y]){

case 0: return Assets.casillaG; break;case 1: return Assets.casillaB; break;case 2: return Assets.casillaR; break;case 3: return Assets.barco; break;default: return Assets.barco2; break;

}}

-Actualizar valores de las variables.

En resumen todo lo que no sea dibujar en pantalla y deba ejecutarse siempre se ha de poner fuera del renderizado, pero teniendo en cuenta que el render lo debe llamar, si hicieramos la parte logica en una clase llamada World deberia quedar algo parecido a esto:

public class World {

//atributos internos

//constructor

public World(){initialize();//metodo interno, abajo creado.

}

//inicializar variables o limpiar

public void initialize(){

}

//actualizacion

public void update(){//aqui deberá ir todo lo que forma parte de la lógica para el renderizado de la

pantalla, teniendo en cuenta que hay que llamarlo desde el render, que se el puede pasar los parámetros que se necesiten

}}

Puede haber distintos World, y puede usarse en las pantallas que se necesiten, no tiene porque ser solo de una.

Para usar el World hay que llamarlo desde el render, pero antes tiene que haber un objeto desde el que llamarlo, que sera creado en la clase de la pantalla, aqui un ejemplo:

public class GameScreen implements Screen{

World world;//Atributos

public GameScreen(){

this.world = new World();//esto llama al contructor, que a la vez llama al inizialite.

//inicializacion de atributos.

}

@Overridepublic void dispose() {}

@Overridepublic void hide() {}

@Overridepublic void pause() {}

@Overridepublic void render(float delta) {//delta es el tiempo transcurrido desde la ultima llamada

por si la logica lo necesita.World.update();//aqui actualizamos la logica del juego.//Pintar imágenes.

}

@Overridepublic void resize(int width, int height) {}

@Overridepublic void resume() {}

@Overridepublic void show() {}

}

Recursos para Desarrollo de JuegosDescargar Sonidoshttp://www.mysoundfx.com/

Musica para dar ambiente a tu juegohttp://pro.jamendo.com/es/

Tutorial creación Sprites Animadas desde cerohttp://ilovepixel.net/tutorial.html

2d Game Art For Programmershttp://2dgameartforprogrammers.blogspot.com/

Crear un personaje para Animaciónhttp://2dgameartforprogrammers.blogspot.com/2011/10/creating-game-character.html

Carashttp://2dgameartforprogrammers.blogspot.com/2011/11/creating-basic-face.html

Fondoshttp://2dgameartforprogrammers.blogspot.com/2011/10/more-fun-with-gradients.html