SpriteBatch类详解

SpriteBatch类详解

在之前所有的例子中,涉及到画图我们都是使用SpriteBatch来处理的,这里把SpriteBatch详细说明一下。

绘图

图片在存储时通常具有一定的格式,我们这里只说png类型,当一个png的文件被读入到GPU(图形处理器)后,我们称其为texture(纹理)。为了在屏幕上画一张图(一个纹理),我们需要设定几何图形来与该图片(纹理)进行对应,比如我们可以设定一个矩形,然后图片的四个角与该矩形的四个角对应。如果这个矩形只占该图片的一部分,我们称其为texture region(纹理区域),这个概念在我们从一个精灵集中摘取一部分图像十分有用。

在实际进行绘制时,首先纹理需要被装载(bind),然后需要告诉OpenGL一个几何形状,该纹理所占的大小和位置需要根据该纹理的几何形状与viewport的设定来处理。许多2D游戏都将viewport设定为与屏幕的像素一致,也就是说,几何图形是以像素为单位的,这样十分容易处理图像在屏幕上的大小和位置。

实际应用中,绘制一个矩形的图像是十分常见的,反复绘制同一个矩形或图像的某一个部分也是很常见的。因此,如果每次需要绘制的时候都向GPU发送矩形信息是没有效率的,相反,我们可以将描述同一个纹理的全部矩形信息整理好,然后一次性发给GPU,这样就可以大大提高GPU的效率。SpriteBatch类就是用来做这个打包的动作。

我们把每个需要绘制的纹理及其坐标发给SpriteBatch类的对象,该对象会收集所有的几何图形信息,如果最新的纹理与上一次的不一样,它会更新为最新的纹理信息,然后统一发送给GPU进行绘制,然后再次收集下一次需要绘制的纹理信息。

每次更改少量的矩形会导致SpriteBatch无法大量的预存更多几何图形,在代码中,频繁的读取图像文件也是不被建议的,通常的做法是,将大量图形素材打包成一个图像文件,然后每次仅绘制其中几个区域,以此来最大的利用SpriteBatch的能力。打包图像请参考TextruePacker

SpriteBatch

在应用中使用SpriteBatch代码如下

public class Game implements ApplicationListener {

private SpriteBatch batch;

public void create () {

batch = new SpriteBatch();

}

public void render () {

Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // This cryptic line clears the screen.

batch.begin();

// Drawing goes here!

batch.end();

}

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

public void pause () { }

public void resume () { }

public void dispose () { }

}

所有SpriteBatch的绘图函数,必须放在begin和end函数之间,在begin和end之间不能放非SpriteBatch的其他绘图方法。

Texture

Texture类用来读取一个图像文件,将其load到GPU的内存中,该图像文件应该放到assets目录下,每个图像的尺寸必须是2的幂,比如16x16, 16x256,最大不建议超过1024x1024

private Texture texture;

...

texture = new Texture(Gdx.files.internal("image.png"));

...

batch.begin();

batch.draw(texture, 10, 10);

batch.end();

以上代码就是读取image.png文件,并将其在(10,10)的位置上绘制出来,其宽、高与图形的实际尺寸相等。SpriteBatch的draw支持以下调用方式绘图

| `draw(Texture texture, float x, float y)` |

最简单的调用方式,按照纹理实际的宽高在(x,y)位置绘图

| `draw(Texture texture, float x, float y, int srcX, int srcY, int srcWidth, int srcHeight)` |

仅绘制一部分

| `draw(Texture texture, float x, float y, float width, float height, int srcX, int srcY, int srcWidth, int srcHeight, boolean flipX, boolean flipY)` |

仅绘制一部分,但是该部分会被缩放到指定大小,并支持翻转

| `draw(Texture texture, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation,int srcX, int srcY, int srcWidth, int srcHeight,boolean flipX, boolean flipY)` |

仅绘制一部分,并对该部分进行缩放到指定宽,高,可以选则是否翻转,并且进行比例缩放和旋转

| `draw(Texture texture, float x, float y, float width, float height, float u, float v, float u2, float v2)` |

功能是绘制一部分,并缩放到指定的width和height,但是方法使用了纹理坐标(0~1),而不是像素单位

| `draw(Texture texture, float[] spriteVertices, int offset, int length)` |

这里使用了更底层的处理方法,可以用来绘制任意的四边形,不单单是矩形绘制

TextureRegion

TextureRegion类表示了一个图像(纹理)里的一个矩形,通常被用来绘制一个大图形的某一个部分。

private TextureRegion region;

...

texture = new Texture(Gdx.files.internal("image.png"));

region = new TextureRegion(texture, 20, 20, 50, 50);

...

batch.begin();

batch.draw(region, 10, 10);

batch.end();

这里20,20,50,50描述了图像的某一个部分,该部分会被绘制到(10,10)位置。使用Texture类可以实现同样的效果,但是使用TextureRegion显得更加方便和直观。

SpriteBatch对于TextureRegion的绘制同样提供了多种支持

| `draw(TextureRegion region, float x, float y)` |

使用区域的宽、高在(x,y)位置绘制

| `draw(TextureRegion region, float x, float y, float width, float height)` |

绘制该区域,并且拉伸到指定的width和height

| `draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation)` |

绘制该区域,并拉伸到指定width和height,同时按照origin坐标进行比例调整和旋转

Sprite

Sprite类在TextureRegion的基础上,又增加了几何形状和颜色属性

private Sprite sprite;

...

texture = new Texture(Gdx.files.internal("image.png"));

sprite = new Sprite(texture, 20, 20, 50, 50);

sprite.setPosition(10, 10);

sprite.setRotation(45);

...

batch.begin();

sprite.draw(batch);

batch.end();

这段代码将image.png读进显存,然后取出其中一块区域(20,20,50,50),然后将其旋转45度,绘制到(10,10)位置,用Texture和TextureRegion都可实现该动作,但是使用Sprite可以更加直观和方便。除此之外,spite还会存储纹理的几何形状,这样仅仅在需要的时候才会重新计算几何形状,这样,如果每一帧中,比例尺,旋转以及其他属性不变化的话,使用sprite会更加高效。

注意的是,使用Sprite会混合model(位置,旋转)和view(纹理本身)的信息,因此如果坚持使用mvc分离的设计模式,那么使用sprite就不太方便了,这时应该使用Texture或TextureRegion。

另一个需要注意的是,Sprite构造函数没有与位置信息相关的参数,在调用Sprite(Texture,int, int,int,int)是不会处理任何位置信息。你必须通过调用setPosition(float,float)来处理位置信息,否则sprite会绘制到(0,0)位置。

Tint(上色)

当绘制一个纹理(texture)时,可以为它上额外的颜色(tinting)

private Texture texture;

private TextureRegion region;

private Sprite sprite;

...

texture = new Texture(Gdx.files.internal("image.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();

这段例子显示了,如何对Texture,TextureRegion和Sprite进行上色,颜色的描述是按照RGBA,四个在0到1之间的数,如果没有使能blending,A值被忽略。

Blending

Blending缺省是使能的,这意为着在绘制一个图像时,该图像中透明的部分会直接与屏幕上已有的纹理重合,显示出屏幕上已有的图像。

如果关闭Blending,那么屏幕上原有的图像会被新的纹理替代,这样在绘制背景时会更有效率,在实际应用中,也要尽量可能关闭blending,除非你确认需要这个效果。

在下面的例子中,在开始绘制时关闭blending会大大提升性能

Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // This cryptic line clears the screen.

batch.begin();

batch.disableBlending();

backgroundSprite.draw(batch);

batch.enableBlending();

// Other drawing here.

batch.end();

注意:在绘制开始前,最好做一个清屏Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);,如果不这样做,反复绘制一个具有alpha属性的图像会导致成百个图像的重叠,最后图像越来越不清晰。另外,一些GPU也倾向与在干净的屏幕上绘制,这样会获得更好的性能。

Viewport

SpriteBatch管理着自己的投射和变换矩阵,当创建一个SpriteBatch时,它会使用当前应用/游戏的设定来建立一个orthographic 投射,使用y轴向上的坐标系(原点在左下角),当调用Begin时,SpriteBatch会建立viewport。

性能优化

SpriteBatch提供了一个构造函数,来设定在向GPU发送前缓存的最大sprite数量,这个值如果太低,会导致向GPU额外的调用,如果太高,会浪费显存。

SpriteBatch具有一个公共的int 对象maxSpritesInBatch,该值记录了该SpriteBatch整个生命周期中向GPU发送的最高Sprites数量(峰值),首先设定一个较大的SpriteBatch,然后通过检查该maxSpritesInBatch来进行调整,可以获得更合理的设置。该值可以在任意时候设为0来进行复位。

SprintBatch具有一个公共的int对象renderCalls,在结束end调用后,该值记录了上一个begin到end周期中,向GPU发送了多少次渲染调用。该值仅仅在图像被装载(bind)或者当SpriteBatch没有被充满时存在。如果SpriteBatch是大小合适的,而renderCalls偏大(超过15~20),表示存在了过多的图像装载。

SpriteBatch具有另外一个构造函数,接受大小和缓冲数量为参数,这是一种更接近底层的用法,可以使用VBO而不是使用传统的VA。会保留一个缓冲区列表,每次的render会使用该列表的下一个缓冲区。当maxSpriteInBatch较低,renderClalls很大时,使用这种方法可以极大的改善性能。

时间: 2024-11-09 01:51:50

SpriteBatch类详解的相关文章

QAction类详解:

先贴一段描述:Qt文档原文: Detailed Description The QAction class provides an abstract user interface action that can be inserted into widgets. In applications many common commands can be invoked via menus, toolbar buttons, and keyboard shortcuts. Since the user

Android技术18:Android中Adapter类详解

1.Adapter设计模式 Android中adapter接口有很多种实现,例如,ArrayAdapter,BaseAdapter,CursorAdapter,SimpleAdapter,SimpleCursorAdapter等,他们分别对应不同的数据源.例如,ArrayAdater对应List和数组数据源,而CursorAdapter对应Cursor对象(一般从数据库中获取的记录集).这些Adapter都需要getView方法返回当前列表项显示的View对象.当Model发生改变时,会调用Ba

C++虚基类详解

1.虚基类的作用从上面的介绍可知:如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员.在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员,如    c1.A::display( ).在一个类中保留间接共同基类的多份同名成员,这种现象是人们不希望出现的.C++提供虚基类(virtual base class )的方法,使得在继承间接共同基类时只保留一份成员.现在,将类A声明为

URLConnection类详解

为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3753224.html URLConnection概述 URLConnection是一个抽象类,表示指向URL指定资源的活动连接. URLConnection类本身依赖于Socket类实现网络连接.一般认为,URLConnection类提供了比Socket类更易于使用.更高级的网络连接抽象.但实际上,大多数程序员都会忽略它

ThreadLocal类详解

众所周知,ThreadLocal对象可以每一个线程保存一份值,可以避免因线程间共享数据带来的问题. 其实现的原理,大致如下,具体的可以参考JDK里的源码. Thread类中,有一个threadLocals字段,它是ThreadLocalMap类型(ThreadLocal里的一个静态内部类).该字段存放当前线程下,所有与ThreadLocal相关的值.该对象是一个Map,key为ThreadLocal对象,value为所存放的值. 在ThreadLocal类里,有两个重要的方法:set()和get

Cocos2d之Node类详解之节点树(二)

一.声明 本文属于笔者原创,允许读者转载和分享,只要注明文章来源即可. 笔者使用cocos2d框架的cocos2d-x-3.3rc0版本的源代码做分析.这篇文章承接上篇<Cocos2d之Node类详解之节点树(一)>. 二.简介 节点 一个Node对象. 节点树 上篇文章介绍到,Node类有一个成员变量 Vector<Node*> _children,这是一个保存所有子节点的数组,因为Node类采用遍历树的方式获取子节点进行渲染,所以我管这两个东西的结合叫节点树. 三.源码详解 &

Android开发之Html类详解

在进行Android开发中经常回忽略Html类.这个类其实很简单,就是将HTML标签文本解析成普通的样式文本.下面就让我么看一下这个类的具体介绍. 类结构: java.lang.Object    ? android.text.Html 类概述: 这个类用于处理的HTML字符串并将其转换成可显示的样式文本.但并不是所有的HTML标记的支持. 公有方法: 说其简单是应为它就有四个方法: Public Methods static String escapeHtml(CharSequence tex

Opencart 之 Registry 类详解

Registry 中文意思是记录,登记,记录本的意思, 在opencart中他的用途就是 登记公共类.类的原型放在 system\engine文件夹下 代码很简单: <?php final class Registry { private $data = array(); public function get($key) { return (isset($this->data[$key]) ? $this->data[$key] : NULL); } public function s

Cocos2d之Node类详解之ZOrder详解

一.声明 笔者以cocos2d框架的cocos2d-x-3.3rc0版本源码做分析.本文属于笔者原创,允许转载和分享,但请注明文章出处. 二.简介 ZOrder ZOrder顾名思义就是节点(Node对象)在Z轴上的排序,这样一来ZOrder越小就越优先显示.每个节点(Node对象)可以持有多个子节点,组成节点树(关于节点树的介绍查看<Cocos2d之Node类详解之节点树>一文).ZOder表示了节点树中每个子节点显示的优先级.值得注意的是,节点树中子节点的ZOder可能会一样,这种情况下父