When making a video game of any description or genre, every one will use a secondary or even tertiary buffer that performs game logic before it is rendered on screen. Buffers are useful because they can reduce performance costs on the main thread when calling the same routine every frame. You should consider redrawing the secondary buffer only when it changes.

With today's hardware, you can get away without the use of a back buffer and render everything every frame regardless of changes, but there is another benefit.

You will likely build options menus for your video game, one of which will be display resolution. A Back Buffer can be easily resized and at little cost to the performance. In our 2D context, a back buffer will be stretched to fill the entire game client. This is a cheap method of drawing our back buffer into our main graphics and should be avoided for production code, but we can use this for testing our game.

Later, we should take into account aspect ratio and maintain it when resizing the window our game sits in. Alternatively, we can lock the aspect ratio and disable manual resizing of our window, but this may only work on native targets (Windows, Linux and Mac). Since most of our testing will be done on HTML5, we won't worry about this until later.

Understanding the Back Buffer API

Twinspire in 2D context assumes you will only ever need one back buffer. Thus, the only code available to us is limited but good enough for our purposes.

To create a back buffer, we use the Application.createBackBuffer() function. It takes two parameters, the initial width and height for the buffer.

Let's place this in our current code in our Game class, inside our init function:

function init()
{
    Application.createBackBuffer(1280, 720);
}

This creates a buffer at the same width and height we set for our game client when it first gets displayed. Technically, the width and height set for the game client also includes any window borders, so it might not be an exact size. Width will only need to minus 2, but on Windows the height will need to be adjusted to take into account the top border.

function init()
{
    var topBar = 45; // top bar = 44 pixels high, plus 1 for the pixel at the bottom
    Application.createBackBuffer(1278, 720 - topBar);
}

This is just for native Windows applications. Considering the number of libraries, frameworks and the way operating systems handle windows, covering them all would be a nightmare scenario and you should not take this too seriously.

For now, we will stick with the previous code. 40+ pixels is not a problem in the grand scheme of things and we can go back to this in the future.

Let's try rendering:

function render(g2:Graphics)
{
    var bG2 = Application.getGraphics2D();

    bG2.begin(true, Color.Black);
    bG2.color = Color.Red;
    bG2.fillRect(50, 50, 100, 100);

    bG2.end();

    g2.color = Color.White;
    g2.drawScaledImage(Application.getBufferContext(), 0, 0, System.windowWidth(), System.windowHeight());
}

In the above code, we are obtaining the graphics 2D context, which is the g2 or graphics2 API of Kha, of our created back buffer.

In this buffer, we fill a rectangle at the position 50, 50 and the size of 100, 100.

We need to make sure we begin and end before making any draw calls.

Finally, we can draw that buffer to the main graphics so we can see our results. We use drawScaledImage and pass in Application.getBufferContext, which returns us the kha.Image of the back buffer, followed by the position and size. The size we pass is whatever the size of the current window is.

We need to import relevant classes and make sure we do not wrap g2.begin inside another g2.begin as this won't work.

For completion, this is our end code:

package;

import twinspire.events.Event;
import twinspire.events.EventType;
import twinspire.Application;

import kha.graphics2.Graphics;
import kha.System;
import kha.Color;

@:build(twinspire.macros.StaticBuilder.build())
class Game
{

    @:local
    var mouseX:Int;
    var mouseY:Int;
    var mouseDown:Bool;
    var mouseReleased:Bool;

    @:global
    function init()
    {
        Application.createBackBuffer(1280, 720);
    }

    function handleEvent(e:Event)
    {

    }

    function render(g2:Graphics)
    {
        var bG2 = Application.getGraphics2D();

        bG2.begin(true, Color.Black);
        bG2.color = Color.Red;
        bG2.fillRect(50, 50, 100, 100);

        bG2.end();

		g2.begin(true, Color.Black);
        g2.color = Color.White;
        g2.drawScaledImage(Application.getBufferContext(), 0, 0, System.windowWidth(), System.windowHeight());
		g2.end();
    }

}

And remove the g2.begin and g2.end from our Main class so we are left with this:

static function render(buffers:Array<Framebuffer>)
{
    var app = Application.instance;
    var g2 = buffers[0].g2;

    while (app.pollEvent())
    {
        Game.handleEvent(app.currentEvent);
    }

    Game.render(g2);
}

And our actual results when we compile and test:

Previous Tutorial -> Using the Static Builder

Next Tutorial -> Event Handling