The renderer keyword¶
The renderer is the main utility in Xentu used for drawing graphics onto the screen. In terms of functionality it is very similar to the SpriteBatch that you’ll find in Microsoft XNA and MonoGame.
At it’s core, the renderer groups organised batches of sprite calls together, and sends them to the GPU to be rendered. Doing this allows the engine to draw many graphics at once at a very fast rate.
A Quick example¶
Typically the renderer is used within the draw event in your game.lua
file. Here is an example of how you could use it:
game.on('draw', function(arg)
renderer.clear()
renderer.begin()
renderer.draw_sprite( my_sprite )
renderer.draw_sprite( my_sprite2 )
renderer.present()
end)
Notice that we use a clear()
function before everything which clears the frame
buffer. Then we call begin()
and present()
call surrounding the actual
call to draw two sprites. This group constitutes as a single drawing layer. As
long as you finish a layer with the present()
call, you can begin a new one
straight after like so:
game.on('draw', function(arg)
renderer.clear()
-- layer #1
renderer.begin()
renderer.draw_sprite( my_sprite )
renderer.draw_sprite( my_sprite2 )
renderer.present()
-- layer #2
renderer.begin()
renderer.draw_sprite( my_sprite3 )
renderer.present()
end)
The above may not seem that useful at first. However once you start building your own games, you will understand why this can be so powerful.
Basic drawing¶
renderer.clear()¶
The clear method tells the renderer to clear the frame buffer and should be called at the beginning of your draw callback.
renderer.begin()¶
The begin method tells the renderer to clear it’s instruction buffer, and prepare
for new draw instructions. If you dont do this after calling present()
. The
result from the previous draw call will still be stored in the instruction buffer.
renderer.present()¶
The present method tells the renderer to send all queued instructions from the instruction buffer to the GPU for rendering.
renderer.draw_sprite(sprite)¶
This method tells the renderer to queue an instruction in it’s buffer to draw a sprite. The sprite argument must be an instance of the Sprite struct setup earlier in your code.
renderer.draw_rect(color, x, y, width, height)¶
This method tells the renderer to draw a rectangle of a specific color, position, and dimensions.
renderer.draw_tilemap_layer(layer)¶
Use this method to draw an individual layer from a loaded tilemap. To get a layer, first you will need to do something like the following in your init event handler:
tilemap = assets.load_tilemap("level1.tmx")
layer0 = tilemap.get_layer(0)
Then in your draw event you could do something like:
renderer.draw_tilemap_layer(layer0)
A good use case for drawing layers individually, is that it allows you to do other things between rendering each layer. For example if you have a tilemap that has a ground, and foreground/sky layer. You could render a player sprite between those two layers. Alternatively you can also apply different shaders to each drawn layer for advanced effects.
renderer.draw_text(font, text, x, y, max_width)¶
This method tells the renderer to queue an instruction in it’s buffer to draw text onto the screen. The method requires you provide an instance to a Font struct , some text, an x-y position, and an optional maximum width before wrapping onto the next line.
Blending graphics¶
renderer.set_blend(bool)¶
The set_blend method is used to specify weather or not to use alpha blending when rendering. By default it is set to true, however you can switch it off if needed.
renderer.set_blend_func(sfactor, dfactor)¶
This method allows you to customise how the blending of graphics works. sfactor
is
the source factor, and dfactor
is the destination factor. The available
values for each argument are as follows:
ZERO
ONE
SRC_COLOR
ONE_MINUS_SRC_COLOR
DST_COLOR
ONE_MINUS_DST_COLOR
SRC_ALPHA
ONE_MINUS_SRC_ALPHA
DST_ALPHA
ONE_MINUS_DST_ALPHA
CONSTANT_COLOR
ONE_MINUS_CONSTANT_COLOR
SRC_ALPHA_SATURATE
SRC1_COLOR
ONE_MINUS_SRC1_COLOR
SRC1_ALPHA
ONE_MINUS_SRC1_ALPHA
A new method is currently in the works called set_blend_preset()
that will
instead only require more commonly known blend terms as described by the Mozilla
foundation’s documentation here.
This should be arriving over the next few weeks.
renderer.set_clear_color(color)¶
This method allows you to pick a color to clear the background with just before
present()
sends new drawing calls to the GPU. The color argument requires
creating a new instance of the Color struct. Here is an example of how to
use it:
local blue = Color.from_hex('#0000ff')
renderer.set_clear_color(blue)
Transforming Coordinates¶
Moving, rotating or scaling the graphics you draw is important for making interesting
games. So Xentu provides some straight forward methods for doing so. Transforms are
reset every time you call the renderer.begin()
method, then can be modified using
the following methods:
renderer.set_origin(x, y)¶
This method sets the transform origin for everything drawn after it. The origin typically starts as 0,0 (top left) meaning if you draw a sprite without any transformation. It’ll appear in the top left without any clipping.
Imagine that sprite is 100x100 pixels in size, and you call set_origin(50, 50)
.
This will move the origin to exactly the centre of that sprite. So when you draw
it, you’ll instead only see the bottom right quater of the sprite.
renderer.set_rotation(angle)¶
This method transforms the next rendered graphic by a angle based rotation. If we
expand on the set_origin()
example from above, using set_rotation(45)
would effectively rotate the sprite around it’s centre by 45 degrees clockwise.
renderer.set_scale(x, y)¶
This method transforms the next rendered graphic by scale. So if you wrote the following:
renderer.set_scale(2, 2)
The next drawn graphic would be 200% tall, and 200% wide.
renderer.set_position(x, y)¶
This method transforms the next rendered graphic by moving (translating). So if you wrote the following:
renderer.set_position(10, 2)
The next drawn graphic would move by 10 pixels to the right. Note that some drawing
methods also let you set a position. When using those after calling set_position
,
both position values will be added up.
Using Shaders¶
renderer.set_shader(shader)¶
The set_shader()
method allows you to tell the renderer what shader program
to use when present()
is called. The shader argument should be the index integer
that you get from assets.load_shader()
earlier in your code.