12/08/2018, 09:35

LibGDX Tutorial 2: Đồ họa trong libGDX

Đây là tutorial mà mọi người luôn cảm thấy thú vị nhất, đó là đưa đồ họa lên màn hình ứng dụng. Chúng ta sẽ sử dụng những cách đơn giản nhất để làm việc này. Hình ảnh sử dụng trong bài viết: http://opengameart.org/content/lpc-girl-variant-2 Chúng ta sẽ sử dụng hình ảnh sau đây: Mình đặt ...

Đây là tutorial mà mọi người luôn cảm thấy thú vị nhất, đó là đưa đồ họa lên màn hình ứng dụng. Chúng ta sẽ sử dụng những cách đơn giản nhất để làm việc này. Hình ảnh sử dụng trong bài viết:

http://opengameart.org/content/lpc-girl-variant-2

Chúng ta sẽ sử dụng hình ảnh sau đây:

dante.png

Mình đặt tên file ảnh này là dante.png và để trong thư mục assets của Android project. Bắt đầu với đoạn code sau:

package com.thinhhung.basicgraphic;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class BasicGraphic implements ApplicationListener {
    private SpriteBatch batch;
    private Texture texture;
    private Sprite sprite;

    @Override
    public void create() {
        batch = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("dante.png"));
        sprite = new Sprite(texture);
    }

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

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

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

Sau đó chạy nó:

Dante.PNG

Hình ảnh sẽ được hiển thị tại điểm gốc của màn hình. Trong trường hợp của LibGDX (0,0) là góc dưới bên trái của màn hình.

Có một điều cần chú ý là Texture (và các lớp tương tự khác) được implement interface Disposable. Điều này có nghĩa là khi hoàn thành việc hiển thị, chúng ta cần sử dụng method dispose(), hoặc không bạn sẽ bị tốn tài nguyên bộ nhớ.

package com.thinhhung.basicgraphic;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
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 BasicGraphic implements ApplicationListener {
    private SpriteBatch batch;
    private Pixmap pixmap;
    private Texture texture;
    private Sprite sprite;

    @Override
    public void create() {
        batch = new SpriteBatch();
        // Width: 256, height: 128
        pixmap = new Pixmap(256, 128, Pixmap.Format.RGBA8888);
        // Fill red color
        pixmap.setColor(Color.RED);
        pixmap.fill();
        // Draw 2 lines
        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 in the middle
        pixmap.setColor(Color.YELLOW);
        pixmap.drawCircle(pixmap.getWidth() / 2, pixmap.getHeight() / 2, pixmap.getHeight() / 2 - 1);
        texture = new Texture(pixmap);
        pixmap.dispose();
        sprite = new Sprite(texture);
    }

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

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

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

Chúng ta vừa vẽ đồ họa sử dụng Pixmap, và đây là kết quả:

Pixmap.PNG

Để tạo một prite sheet, chúng ta cần thư mục ảnh như sau:

Images.PNG

Tải libGDX Texture Packer GUI tại đây: https://code.google.com/p/libgdx-texturepacker-gui/

Đây là màn hình giao diện của libGDX Texture Packer, bạn nhấn vào New pack để tạo 1 pack, ở mục Input directory chọn tới thư mục ảnh, mục Output directory chọn tới thư mục assets của project, File name để là spritesheet:

TexturePackerGUI.PNG

Nhấn nút Pack selected để chạy. Sau khi chạy bạn sẽ được 2 file .atlas và .png. File .atlas là file mô tả hình ảnh spritesheet, nội dung file spritesheet.atlas như sau:

spritesheet.png
format: RGBA8888
filter: Nearest,Nearest
repeat: none
0001
  rotate: false
  xy: 1, 1
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
0003
  rotate: false
  xy: 1, 1
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
0002
  rotate: false
  xy: 51, 1
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
0004
  rotate: false
  xy: 101, 1
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1

Còn đây là hình ảnh spritesheet:

spritesheet.png

Sau khi có được texture atlas, làm thế nào để sử dụng nó? Rất đơn giản, đoạn code sau sẽ hướng dẫn bạn cách sử dụng TextureAtlas:

package com.thinhhung.basicgraphic;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
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;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.utils.Timer;

import java.sql.Time;

public class BasicGraphic 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("spritesheet.atlas")));
		TextureAtlas.AtlasRegion region = textureAtlas.findRegion(currentAtlasKey);
		sprite = new Sprite(region);
		sprite.setPosition(120, 100);
		sprite.scale(2.5f);
		Timer.schedule(new Timer.Task() {
			@Override
			public void run() {
				currentFrame++;
				if (currentFrame > 4) {
					currentFrame = 1;
				}

				currentAtlasKey = String.format("%04d", currentFrame);
				sprite.setRegion(textureAtlas.findRegion(currentAtlasKey));
			}
		}, 0, 1/5.0f);
	}

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

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

	@Override
	public void resize(int awidth, int height) {

	}

	@Override
	public void pause() {

	}

	@Override
	public void resume() {

	}
}

Sau khi chạy ứng dụng chúng ta sẽ thấy được hoạt cảnh đi bộ của nhân vật:

walk.PNG

Để làm việc với TextureAtlas, bạn chỉ cần viết đoạn code sau:

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

Giống như việc bạn làm việc với Texture, thay vào đó là TextureAtlas. Thay vì gán texture vào sprite, bạn sử dụng AtlasRegion mô tả các tọa độ của các sprite độc lập trong srpitesheet. Bạn nhân được region thông qua phương thức findRegion(), truyền vào tham số là các key. Hãy nhớ rằng các key được tạo thành bởi tên các file ảnh ban đầu. TextureAtlas cũng cần dispose() để tránh chiếm nhiều bộ nhớ.

Và bạn có thể thấy đoạn code sau:

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

Bạn có thể thay đổi các region bằng cách gọi phương thức setRegion(). Phần còn lại của đoạn code đơn giản là vị trí và kích thước của các sprite được nhân lên 2.5 lần. Sau đó lên lịch chạy cho các Task sử dụng Timer.schedule(). task này sẽ được gọi 5 lần mỗi giây. Trong trường hợp này, các file được đặt tên là 0001.png, 0002.png, v.v.. Vì vậy chúng ta cần các giá trị từ 0001 đến 0004. Chúng ta sử dụng giá trị này để cập nhật region cho các sprite. Và kết quả mỗi 0,2 giây, các sprite sẽ được di chuyển tới frame tiếp theo, quay trở lại khi tới frame cuối.

Bạn có có thể tham khảo source code của project này tại đây.

0