I present to you the translation of the second tutorial on libGDX. The original is
here . The first part is
here .
This tutorial gives an idea of how to draw images using OpenGL and how libGDX simplifies and optimizes this process using the SpriteBatch class.
Drawing images.
')
The image obtained from its original format (for example, PNG) and uploaded to the GPU is called a texture. Textures are drawn according to some specification, which is a description of a geometric figure and how the texture is superimposed on the vertices of this figure. For example, a geometric shape can be a rectangle, and each corner of the rectangle refers to the corresponding angle of the texture.
For drawing, it is necessary that the texture be made current (attached) and that geometry is specified. The size and location of the place where the texture will be displayed are determined by the geometry and setting of the OpenGL viewport. Many 2D games customize the viewport so that it matches the screen resolution. This means that the geometry is defined in pixels, which makes it easy to draw textures of the appropriate size and in the right place on the screen.
Very often texture drawing takes place in a rectangular geometry. Also very often one texture or its different parts are drawn many times. It is inefficient to send a single rectangle for drawing to the GPU. Instead, many rectangles for one texture can be described and sent to the GPU all together. This is what the SpriteBatch class does.
SpriteBatch gets the texture and coordinates of each rectangle where this texture will be displayed. It accumulates this information without being sent to the GPU. When it receives a texture that is different from the last loaded texture, it activates the last loaded texture, sends the accumulated drawing information to the GPU, and begins to accumulate drawing data for the next texture.
Changing the texture every few rectangles that need to be drawn prevents SpriteBatch from grouping a lot of rectangles. Also, texture snapping is quite an expensive operation. Considering these reasons, they often store many small images in one large image and then draw parts of a large image, maximizing the number of accumulated rectangles for drawing and avoiding texture changes. See
TexturePacker for more details.
SpriteBatch (sprite packer)
Using the SpriteBatch class in an app looks like this:
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);
All SpriteBatch calls for rendering should be enclosed between the begin () and end () methods. Calls to methods for drawing by other means (not the SpriteBatch class) should not occur between the begin () and end () methods.
Texture
The Texture class gets an image from a file, and loads it into the GPU. The image file must be located in the "assets" directory, as described in (REFERENCE) Project Setup. Image sizes should be a power of two (16x16, 64x256, etc.).
private Texture texture; ... texture = new Texture(Gdx.files.internal("image.png")); ... batch.begin(); batch.draw(texture, 10, 10); batch.end();
This creates a texture and sends it to the SpriteBatch class for drawing. The texture is drawn in a right-angled, the lower left corner of which is located at the point (10, 10), with a width and height equal to the size of the texture. SpriteBatch has many methods for drawing textures:
draw(Texture texture, float x, float y)
Draws a texture at x, y, using texture dimensions.
draw(Texture texture, float x, float y, int srcX, int srcY, int srcWidth, int srcHeight)
Draws a piece of texture.
draw(Texture texture, float x, float y, float width, float height, int srcX, int srcY, int srcWidth, int srcHeight, boolean flipX, boolean flipY)
Draws a part of the texture that is stretched to the width * height, and possibly reflected.
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)
This monstrous method draws a part of the texture with the possibility of compression (stretching), rotation around a point and the possibility of reflection.
draw(Texture texture, float x, float y, float width, float height, float u, float v, float u2, float v2)
Draws a part of the texture stretched to the width * height. This is a more advanced method. Coordinates are not in pixels, but in real numbers.
draw(Texture texture, float[] spriteVertices, int offset, int length)
Draws a part of the texture, “dragging” it onto the shape specified in spriteVerticles
TextureRegion (region of the texture or part of the texture)
The TextureRegion class describes a rectangle inside a texture and is used to draw only parts of the texture.
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();
Here 20, 20, 50, 50 describes the part of the texture, which will then be drawn at the point (10, 10). The same action can be done by passing the texture and additional parameters to the SpriteBatch class, but the TextureRegion class makes it more convenient, since it is easier to define a single object and work with it than to remember about a bunch of additional parameters.
SpriteBatch has many methods for drawing a TextureRegion:
draw(TextureRegion region, float x, float y)
- draws a region using the width and height of the region.
draw(TextureRegion region, float x, float y, float width, float height)
- draws a region compressed (stretched) to the size of width and height.
draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation)
- draws a region stretched (compressed) to width and height, with the possibility of scaling and rotation relative to the point originX, originY.
Sprite (Sprite)
The Sprite class describes the region of the texture, the position where this region will be drawn and the color it will be for the region (the color for tinting is color shading).
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();
Here 20, 20, 50, 50 describes the region of the texture that will be rotated 45 degrees and then drawn at (10, 10). The same can be done by passing the texture or part of it to SpriteBatch and passing other parameters, but the Sprite class makes it more convenient because you store all the parameters in one place. Also, due to the fact that the Sprite class stores geometry within itself and recalculate it only if necessary, this slightly improves performance in scaling, rotating, or other properties that do not change between frames.
It should be noted that the Sprite class mixes information about the model (location, information about rotation and others) with information about the view (the texture was drawn by the same class). This makes it inappropriate to use Sprite in architecture where you want to strictly separate the model from the presentation. In this case, using the Texture class or TextureRegion can make more sense.
Tinting (color (shading) color)
When the texture is drawn, it can be painted over with a specific color:
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();
This code shows how to draw a texture, its region and a sprite with a coloring color. The color is described in the RGBA model, where each component lies in the range from 0 to 1. The alpha channel is ignored if blending is turned off.
Blending
Blending is enabled by default. This means that when a texture is drawn, the transparent parts of this texture can be combined from the pixels that are already drawn on the screen in this place.
When blending is turned off, everything that was on the screen is erased by the texture that is drawn in this place. This is more efficient and, thus, you can always turn off the mixing, if you do not need it. For example, if you draw a large background image on the full screen, performance can be improved primarily by disabling blending:
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
Note. Make sure the screen is cleared every frame. If this is not the case, the alpha channel texture will be drawn on top of itself many times, which will give the wrong effect. Also, some graphics accelerator architectures work better if you clear the screen every frame, instead of drawing the image on the whole screen.
Viewport (viewport)
SpriteBatch has its own projection and transformation matrix. When a SpriteBatch is created, it uses the dimensions of the current application to adjust the orthogonal projection using the coordinate system with the Y axis pointing up (i.e., 0, 0 is the bottom left corner of the screen - interpreter's note). When the begin () method is called, SpriteBatch sets its viewport.
Note. As soon as more detailed documentation on the viewing area appears, it will be posted here.
Performance improvement.
SpriteBatch has a constructor that accepts the maximum number of sprites that will be accumulated before being sent to the GPU. If this number is too small, there will be a lot of unnecessary calls to the video accelerator. If the number is large, SpriteBatch will use too much memory.
SpriteBatch has a public field called maxSpritesInBatch. It indicates the maximum number of sprites that can be sent to render the video accelerator at a time during the SpriteBatch life cycle. You can set this number to be very large and check it. This will help you choose the optimal size of SpriteBatch. The size of SpriteBatch (the number you pass to the constructor) should slightly exceed the maxSpritesInBatch number. You can always set maxSpritesInBatch to zero — that is, reset this counter.
SpriteBatch has a public field renderCalls. After the next rendering cycle, it stores the number of times SpriteBatch sent different data about the geometry between the begin () and end () calls. This happens if different textures bind, or if the SpriteBatch was full (the cache is too small). If the size of SpriteBatch is correct and renderCalls is too large (around 15-20), this indicates that you use too many textures. Try to place some of the textures in one big texture.
SpriteBatch has an additional constructor that accepts the number and size of buffers. This is an advanced feature that orders to work with VBO (vertex buffer objects) instead of the usual VA (vertex arrays). SpriteBatch stores a list of buffers and uses the next buffer for each successive rendering cycle. When maxSpritesInBatch is small and the renderCalls is large, this chip can give a small performance boost.