pixelboyz logo
Desarrollo de Videojuegos

LibGDX Tutorial 3: Gráficos básicos

jet

Índice de contenido


Esta es la parte que la gente siempre encuentra más divertida, el acto real de poner gráficos en la pantalla. Comencemos con el proyecto más simple que podamos.

Vamos a mostrar este sprite (creado en este tutorial):

En la pantalla. Una cosa importante a tener en cuenta, el gráfico anterior es de 512 × 256. OpenGL en general y LibGDX en particular, requieren que sus archivos de imagen tengan el poder de dos dimensiones. Esto significa que su ancho y alto son 2,4,8,16,32,64,128,256,512,1024,2048, etc… píxeles de tamaño. Asegúrese de agregar este archivo a la carpeta de datos de activos en el proyecto de Android antes de continuar.

Saltemos directamente con el código:

package com.gamefromscratch.graphicsdemo;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class GraphicsDemo implements ApplicationListener {
    private SpriteBatch batch;
    private Texture texture;
    private Sprite sprite;
    
    @Override
    public void create() {        
        batch = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("data/jet.png"));
        sprite = new Sprite(texture);
    }

    @Override
    public void dispose() {
        batch.dispose();
        texture.dispose();
    }

    @Override
    public void render() {        
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        batch.begin();
        sprite.draw(batch);
        batch.end();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

Y si lo ejecutas:

imagen

La imagen se dibuja en relación con el origen. En el caso de LibGDX (0,0) es la esquina inferior izquierda de la pantalla.

En cuanto al código, en realidad no hay mucho nuevo en comparación con el ejemplo de Hello World en el tutorial anterior. Los únicos conceptos nuevos son la Textura y el Sprite. La textura representa la textura OpenGL subyacente. Una cosa importante a tener en cuenta con Texture (y otras clases similares) es que implementan la interfaz Desechable. Esto significa que cuando haya terminado, debe llamar al método dispose(), ¡o perderá memoria! Un Sprite contiene los datos de geometría y color de una textura, esto significa que los datos de posición (como su ubicación X e Y) se almacenan en el Sprite. Construimos nuestra textura pasando su ruta, obtenida de la misma manera que accedemos a la fuente en el tutorial anterior. Luego construimos el Sprite pasando nuestra textura recién creada. Hay otras formas de crear Sprites, que veremos en breve. Al igual que en el ejemplo de Hello World, comenzamos un SpriteBatch y dibujamos nuestro sprite usando el método draw().

Texturas dinámicas con Pixmap

La fuente de su textura no tiene que provenir de un archivo. Aquí vamos a usar la clase Pixmap para crear dinámicamente la fuente de la textura.

package com.gamefromscratch.graphicsdemo;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class GraphicsDemo implements ApplicationListener {
    private SpriteBatch batch;
    private Pixmap pixmap;
    private Texture texture;
    private Sprite sprite;
    
    @Override
    public void create() {        
        batch = new SpriteBatch();
        
        // A Pixmap is basically a raw image in memory as repesented by pixels
        // We create one 256 wide, 128 height using 8 bytes for Red, Green, Blue and Alpha channels
        pixmap = new Pixmap(256,128, Pixmap.Format.RGBA8888);
        
        //Fill it red
        pixmap.setColor(Color.RED);
        pixmap.fill();
        
        //Draw two lines forming an X
        pixmap.setColor(Color.BLACK);
        pixmap.drawLine(0, 0, pixmap.getWidth()-1, pixmap.getHeight()-1);
        pixmap.drawLine(0, pixmap.getHeight()-1, pixmap.getWidth()-1, 0);
        
        //Draw a circle about the middle
        pixmap.setColor(Color.YELLOW);
        pixmap.drawCircle(pixmap.getWidth()/2, pixmap.getHeight()/2, pixmap.getHeight()/2 - 1);
        
        
        texture = new Texture(pixmap);
        
        //It's the textures responsibility now... get rid of the pixmap
        pixmap.dispose();
        
        sprite = new Sprite(texture);
    }

    @Override
    public void dispose() {
        batch.dispose();
        texture.dispose();
    }

    @Override
    public void render() {        
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        batch.begin();
        sprite.setPosition(0, 0);        
        sprite.draw(batch);
        sprite.setPosition(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2);
        sprite.draw(batch);
        batch.end();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

Una vez más, el código es notablemente similar a nuestro ejemplo anterior. La mayor diferencia es que en lugar de cargar los datos de la imagen de texturas desde un archivo, creamos uno dinámicamente usando un Pixmap. En la forma más simple, un mapa de píxeles se puede considerar como una cuadrícula de datos de píxeles en la memoria. Contiene una serie de funciones gráficas, muchas de las cuales demostramos anteriormente. El código de dibujo está bastante bien comentado en términos de lo que hace, así que no entraré en detalles aquí. Sin embargo, un detalle muy importante, en el mundo moderno impulsado por GPU, este tipo de operaciones por píxel son MUY LENTAS. Por lo general, desea evitarlos tanto como sea posible.

La única otra cosa a tener en cuenta en este ejemplo son los cambios en el método render(). ¿Observe cómo el mismo sprite se dibuja dos veces en el lote de sprites? Bueno, este comportamiento está perfectamente bien y tiene una sobrecarga de rendimiento mínima al hacerlo. El método setPosition de Sprite se usa para posicionar un sprite, una vez más, (0,0) es la esquina inferior izquierda de la pantalla por defecto. El único otro código nuevo aquí son las llamadas a los métodos Gdx.graphics.getWidth() y getHeight(). Estos devuelven las dimensiones de la ventana (o Canvas en el caso de HTML5). Por supuesto, en el código de producción, probablemente los almacenaría en caché localmente en lugar de recuperarlos cada paso a través del bucle de procesamiento.

TexturaAtlas

Muy a menudo querrás lidiar con una hoja de sprites, que es una cantidad de sprites combinados en una sola imagen. Dicha funcionalidad está integrada en LibGdx. Lo primero que necesitará es un directorio de imágenes que se combinarán en una hoja de sprites. Como esto:

imagen

Abra una línea de comandos o una ventana de terminal y ejecute el siguiente comando:

java -cp gdx.jar;extensions/gdx-tools/gdx-tools.jar com.badlogic.gdx.tools.imagepacker.TexturePacker2 c:tmp c:tmp hoja de sprites
tmp

Parece más difícil de manejar de lo que es. Básicamente, está ejecutando la clase TexturePacker2 dentro del contenedor de herramientas gdx. El primer parámetro es el directorio de origen, el segundo parámetro es la dirección de destino y el último parámetro es el nombre de archivo a usar. Agregará automáticamente las extensiones de archivo requeridas. Este proceso creará dos archivos, un archivo .atlas y un .png. El archivo atlas es un archivo de texto que describe cómo se distribuyen los sprites en la imagen de la hoja de sprites, con el nombre del archivo de imágenes utilizado como clave (extensión menos), así:

spritesheet.atlas:

hoja de sprites.png
formato: RGBA8888
filtro: Más cercano,Más cercano
repetir: ninguno
0001
rotar: falso
xy: 1, 651
tamaño: 192, 128
origen: 192, 128
compensación: 0, 0
índice: -1
0002
rotar: falso

Mientras que la hoja de sprites en sí se ve así:

hoja de sprites

La herramienta spritepacker rellena automáticamente la imagen para que tenga un tamaño de potencia de 2. Solo he arañado la superficie misma de lo que esta herramienta puede hacer. Puede configurarlo para que se ejecute como parte de su proceso de compilación, ejecutarlo desde el código o dentro de Eclipse o incluso ejecutarlo en el tiempo de ejecución del programa. Hay una gran cantidad de opciones que puede configurar. puedes leer mucho más sobre esto aquí.

Entonces, ¿cómo se usa realmente un atlas de texturas? Es muy simple, primero copie el archivo png y atlas generado a sus activos. El siguiente código muestra cómo usar un TextureAtlas:

package com.gamefromscratch.graphicsdemo;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.Timer.Task;

public class GraphicsDemo implements ApplicationListener {
    private SpriteBatch batch;
    private TextureAtlas textureAtlas;
    private Sprite sprite;
    private int currentFrame = 1;
    private String currentAtlasKey = new String("0001");
    
    @Override
    public void create() {        
        batch = new SpriteBatch();
        textureAtlas = new TextureAtlas(Gdx.files.internal("data/spritesheet.atlas"));
        AtlasRegion region = textureAtlas.findRegion("0001");
        sprite = new Sprite(region);
        sprite.setPosition(120, 100);
        sprite.scale(2.5f);
        Timer.schedule(new Task(){
                @Override
                public void run() {
                    currentFrame++;
                    if(currentFrame > 20)
                        currentFrame = 1;
                    
                    // ATTENTION! String.format() doesnt work under GWT for god knows why...
                    currentAtlasKey = String.format("%04d", currentFrame);
                    sprite.setRegion(textureAtlas.findRegion(currentAtlasKey));
                }
            }
            ,0,1/30.0f);
    }

    @Override
    public void dispose() {
        batch.dispose();
        textureAtlas.dispose();
    }

    @Override
    public void render() {        
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        batch.begin();
        sprite.draw(batch);
        batch.end();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

Aquí está la versión HTML5 del código anterior:

Como puede ver, es notablemente consistente. La mayoría del código anterior es en realidad parte de la demostración relacionada, en lugar de ser parte del uso de un TextureAtlas. En cambio, el único código nuevo de importación es:

batch = new SpriteBatch();
textureAtlas = new TextureAtlas(Gdx.files.internal("data/spritesheet.atlas"));
AtlasRegion region = textureAtlas.findRegion("0001");
sprite = new Sprite(region);

Es como trabajar con una textura, pero en su lugar carga un TextureAtlas. Luego, en lugar de asignar la textura al sprite, usa AtlasRegion, que describe las coordenadas del sprite individual dentro de la hoja de sprite. Obtiene la región por nombre llamando al método findRegion() y pasando la clave. Recuerde que este valor lo establecen los nombres de archivo de las imágenes de origen. El TextureAtlas debe desecharse () o perderá memoria.

Como se puede ver por la llamada:

sprite.setRegion(textureAtlas.findRegion(currentAtlasKey));

Puede cambiar la región dentro de la hoja de sprite a la que se referirá el sprite llamando a setRegion().

El resto del código simplemente posiciona y escala el sprite 2,5x veces. Luego programamos una tarea usando Timer.schedule(). Esta tarea se llamará cada 30 de segundo. Simplemente cambia la clave que usaremos dentro del TextureAtlas. En este caso, los archivos se llamaron 0001.png, 0002.png, etc., por lo que queremos un valor entre 0001 y 0020. Luego usamos este valor para actualizar la región a la que se refiere el sprite. Como resultado, cada 30 de segundo, el sprite pasa al siguiente cuadro de animación y se da la vuelta cuando llega al final.

EDITAR: Debo decir para que conste, esta NO es la forma en que usaría un TextureAtlas para realizar una animación, este código fue simplemente para fines de demostración. Hay clases de animación dedicadas y las cubriremos más adelante.

Sin embargo, tenga cuidado, si intenta ejecutar esto en GWT, verá:

imagen

Esto se debe a que el compilador GWT (esto no tiene nada que ver con LibGDX) no es compatible con String.format() por algún motivo. Si desea ejecutar este ejemplo en un navegador, simplemente puede reemplazar

currentAtlasKey = String.format("%04d", currentFrame);

Con:

String base = new String();                    if(currentFrame >= 10)              base = "00";          else              base = "000";            currentAtlasKey = base + currentFrame;

Ahora el objetivo HTML debería funcionar bien.

En la siguiente parte, veremos cómo controlar la entrada en LibGDX.

Configuración de LibGDX para usar GL 2

Me llamó la atención por mario zechner que LibGDX no se limita al poder de 2 tamaños de textura. En cambio, es un artefacto de OpenGL ES 1. Si ejecuta OpenGL ES2, funcionará bien. Dicho esto, OpenGL y el hardware subyacente aún funcionan mejor si se limita a la potencia de dos. Si desea usar GL2, lo configura como parte del proceso de configuración, discutimos brevemente en el tutorial hola mundo. Simplemente establezca el valor useGL20 en verdadero en la configuración que pasa a su agente de escucha de la aplicación. Aquí, por ejemplo, está Main desde el proyecto de escritorio configurado para usar GL 2.

public class Main {
    public static void main(String[] args) {
        LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
        cfg.title = "graphicsdemo";
        cfg.useGL20 = true;
        cfg.width = 480;
        cfg.height = 320;
        
        new LwjglApplication(new GraphicsDemo(), cfg);
    }
}

Recuerde, por supuesto, configurar esto para todos los objetivos que está apoyando.

EDITAR:

18/12/2013 – Se me indicó que no incluí los archivos del atlas, lo que hace que este tutorial sea difícil de seguir. He incluido un archivo de la carpeta de datos utilizada para este ejemplo, Puedes descargarlo aquí.

Programación Java Tutorial LibGDX


Parte anterior Tabla de contenido siguiente parte





Source link

Tags :
básicos,gráficos,LibGDX,Tutorial

Comparte :

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *