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.