Introduction
本章介绍2D图形相关的一些基本内容,包括如何使用Texture,TextureRegion和SpriteBatch。
Texture
略。。
SpriteBatch
LibGdx使用SpriteBatch来完成纹理映射并在屏幕上显示被纹理映射的四边形的所有工作。它使得在屏幕上绘制图形极为简单,并且经过优化。它在屏幕坐标系中工作,使用屏幕像素的绝对坐标。左下角是原点(0,0),X轴向右,Y轴向上。由于SpriteBatch可以同时处理多个绘制请求、使用GPU进行渲染加速并序列化请求,所以程序员只要启动它就行。
下面的代码把一张图片绘制到屏幕上:
[java] view plaincopy
- public class TextureFun implements ApplicationListener {
- private Texture druidTexture; // #1
- private SpriteBatch batch; // #2
- @Override
- public void create() {
- druidTexture = new Texture(Gdx.files.internal("druid.png")); // #3
- batch = new SpriteBatch(); // #4
- }
- @Override
- public void render() {
- batch.begin(); // #5
- batch.draw(druidTexture, 100, 100); // #6
- batch.end(); // #7
- }
- // … rest of methods omitted … //
- }
1. 声明一张纹理
2. 声明用来把纹理显示到屏幕的SpriteBatch
3. 创建纹理。图片druid.png需要位于asset文件夹里,图片格式可以是jpg,png或bmp,图片宽度和高度必须是2的n次幂,如1,2,4,8,16,32,64,128...另外,宽度和高度不需要相等
4. 创建SpriteBatch
5. 调用begin()函数后,SpriteBatch开始接收请求
6. 一条绘制请求,它请求SpriteBatch把纹理绘制到(100,100)的位置。注意这个坐标是屏幕绝对坐标。
7. 告诉SpriteBatch暂时没有其他请求了,它可以开始处理刚才收到的请求了。
显示结果和坐标系统如下所示:
你可以通过在begin()和end()函数之间添加多条draw()指令来绘制多个图形,绘制顺序和draw()的调用顺序相同,所以如果有重叠的部分,后来的图片会在覆盖在先前的图片上面。
除了简单的draw(),SpriteBatch还有一些其他功能,如设置混合颜色、设置转换矩阵。不过转换矩阵必须在begin()之前设置。
下面的代码启用SpriteBatch的纹理混合功能并设置混合方法。不过纹理混合在默认就是启用的。
[java] view plaincopy
- ...
- batch.enableBlending();
- batch.setBlendFunction(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
- ...
纹理有多种不同的创建方式,都在源码的注释中有详细介绍。
当需要使用mipmaps(多级纹理)时,Texture会在初始化时就创建它们。MipMaps是指预先计算好的,同一张图片的不同尺寸的副本,在需要缩放图片以适应四边形时,选择最恰当的那个副本,可以提高渲染效率。
draw()函数有多种重载,各种参数的意义在源码注释中都很详细。下面是其中之一:
[java] view plaincopy
- public void render() {
- batch.begin();
- batch.draw(druidTexture, 100, 100);
- batch.draw(druidTexture, 200, 100, 32, 32, 64, 64, 1f, 2.0f, 45f, 0, 0, 64, 64, false, false);
- batch.end();
- }
在注释中有提一些参数是Texel空间的,texel是指导图片上的一个像素,要和屏幕上的一个像素区别开。
TextureRegion
在一个游戏里,必然有很多需要绘制的元素,如果每个元素都要转换成一张纹理,对GPU的资源消耗是很大的,因为在绘制前,GPU都要把图片载入显存并绑定到OpenGL上,然后OpenGL再绘制需要的纹理并在不同纹理间切换,而绑定和切换的代价都是很昂贵的。
TextureRgion能解决这个问题,它从一个纹理上切割出一个区域并让SpriteBatch作用于该区域。这样,一张纹理上可以包含多个需要绘制的元素,而只有与该元素对应的那部分才会绘制出来。包含多个绘制元素的纹理也称为sprite sheet。
下图说明一张纹理如何被分割成多个元素。这种方式能避免在不同纹理间切换的昂贵代价。纹理的大小必须是2的n次幂,但纹理区域可以随意定义。
下面的代码创建并绘制这张纹理的四个区域,还在(0,0)位置显示了一张缩小的纹理。
[python] view plaincopy
- public class TextureFun implements ApplicationListener {
- private Texture texture; // #1
- private SpriteBatch batch;
- private TextureRegion[] regions = new TextureRegion[4]; // #2
- @Override
- public void create() {
- texture = new Texture(Gdx.files.internal("sprite_sheet.png"));
- batch = new SpriteBatch();
- regions[0] = new TextureRegion(texture, 0, 0, 64, 64); // #3
- regions[1] = new TextureRegion(texture, 0.5f, 0f, 1f, 0.5f); // #4
- regions[2] = new TextureRegion(texture, 0, 63, 64, 64); // #5
- regions[3] = new TextureRegion(texture, 0.5f, 0.5f, 1f, 1f); // #6
- }
- @Override
- public void render() {
- batch.begin();
- batch.draw(texture, 0, 0, 64, 64); // #7
- for (int i = 0; i < regions.length; i++) {
- batch.draw(regions[i], 75 * (i + 1), 100); // #8
- }
- batch.end();
- }
- // … rest of methods ommited... //
- }
如果要被分割的纹理区域大小相等且没有间隔,可以用一种更简单的方式来创建TextureRegion:
[java] view plaincopy
- TextureRegion[][] regions = TextureRegion.split(texture, 64, 64)