An Introduction to Drawing in Quorum

In this tutorial, we describe how to draw objects on the game screen in Quorum. This tutorial will cover primary topics: 1) drawing shapes, 2) drawing images, and 3) ImageSheets. The purpose of these topics is to give the reader a primer on how the drawing system works, so that he/she can draw shapes and images in the game correctly and efficiently.

Drawing Shapes

We start this tutorial with the templated Game Application from the New Project window, as described in the Getting Started tutorial. Our basic game code (with comments omitted) looks like this:
use Libraries.Game.Game

class Main is Game
    action Main
        StartGame()
    end

    action CreateGame
    end

    action Update(number seconds)
    end
end
This template creates the architecture that tells Quorum to create a new game and begin execution of the Main Loop. Behind the scenes, this connects to a very powerful utility called OpenGL. OpenGL is an industry standard graphics library specifically designed for drawing graphics. While OpenGL is very powerful, it is not particularly easy to use. In order to understand its details one must have an understanding of a mathematical theory called Linear Algebra, which is an advanced topic that is typically taught at universities. The Quorum Game Engine handles all this complexity for us though so we just need to learn how to call the methods of the built in libraries to generate drawable images and shapes.To draw shapes to our empty game program, we need to add a use statement to our program to tell the compiler where to find the commands for a class in the standard library named Drawable. The code for the library reference is:
use Libraries.Game.Graphics.Drawable

This line of code tells Quorum that we want to use the Drawable class in our application. In other words, it tells the compiler that we want to be able to add items to the screen that will be drawable in the frame drawing portion of the Main Loop. Once we have access to the Drawable class, we need to create and name a drawable object (another word for this is "instantiate"). We can do this by adding another line of code just below the "class Main is Game" line, such as:

use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable rectangle

While this code creates a drawable object, it does not draw anything on the screen or load an image to associate to the object. In order to do that, we need to insert code into the CreateGame action:

action CreateGame

   //This line of code draws a rectangle with a width and height of 50.
   //By default, the coordinates of this object are 0,0, which is the bottom left corner of our window.

   rectangle:LoadFilledRectangle(50, 50)

   //This line of code tells Quorum to add our drawable object to our game, 
   //so that the game engine puts it in the list of items that it draws on each frame.
   //If we forget it, Quorum will have no way to know we want to draw this object.

   Add(rectangle)
end

Once we have these lines of code in our program we can Run our game (F6) which will now look similar to this:

This is an image of a black square on game

There are many kinds of additional shapes that can be drawn by default. We can use these shapes in combination to create complex pictures. The full list of shapes is in the Drawable documentation. The list includes: 1) circles, 2) triangles, 3) rectangles, and 4) lines. In the case of of circles, triangles, and rectangles, the shapes can either be filled or not and a color can also be specified (if none, the default color is black).

Drawing Images

In addition to shapes, we can also display pre-made images on the screen. This is useful if we want to use external programs like Photoshop or Gimp (a free alternative) to make images ahead of time or if we want to use digital photos we've taken. Similarly, there is a large amount of free art on the Internet that can be used for commercial or non-commercial purposes (under the creative commons license, for example).

Quorum can load such images in two ways, by using: 1) a Drawable directly, or 2) an ImageSheet (a set of combined images). First let us discuss using a Drawable to load images.

To load an image we take the same approach we used before by creating a drawable object first:

Drawable logo

Then we can load an image file stored on our hard drive by calling the Load method of the Drawable class. For this example, we will load the logo for the Quorum Hour of Code:

This is an image of the Hour of code Quorum logo

This image is licensed under Creative Commons, so please feel free to download and use it for your own purposes. To load this image, we first need to make a copy of it and place it in the directory of our project. It does not matter where in our project we place it, but for this example we made a new folder named "assets" in the main project directory:

This is an image of the assets in the main project directory

To load the image into our "logo" drawable object, in our CreateGame action we simply call the Load method of the Drawable class with the path and filename of the file in our project and then call the Add action. (Notice that the path of the file is relative to the root of our main project directory.):

logo:Load("assets\hourofcode.png")
Add(logo)

So the whole game code looks like this:

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable logo

    action Main
        StartGame()
    end

    action CreateGame
        logo:Load("assets\hourofcode.png")
        Add(logo)
    end

    action Update(number seconds)
    end
end

...and when we Run the program (F6) we get a game window that looks something like this:

This is an image of the display image when you run

Alternatively, we could use a file class to load the image file into the Drawable. In that case, our code would look like this:

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable
use Libraries.System.File

class Main is Game
    Drawable logo

    action Main
        StartGame()
    end

    action CreateGame
        File file
        file:SetPath("assets\hourofcode.png")
        logo:Load(file)
        Add(logo)
    end

    action Update(number seconds)
    end
end

ImageSheets

In computer graphics, the computer can sometimes render (put on the screen) images faster by using what are often called Texture atlases. While we call these ImageSheets in Quorum, they are basically the same thing. Generally, many casual games can be written without using this technique, but it can speed up certain kinds of programs where there are a lot of objects to display. Essentially ImageSheets allow OpenGL to reduce the amount of loading that it has to do (which can be a very expensive process that must be done on each frame). In this tutorial, we will not get into the details of when to use, or not use ImageSheets, as this is a complicated discussion related to graphics hardware. We will say, however, for the types of 2D games Quorum's engine currently supports, using ImageSheets can often speed up a program. When in doubt, trying a program both ways can give clues, if efficiency is a concern. As you will see, the code for loading images is similar in any case.

To make an ImageSheet, let's first add a second image to our game application. This one is also creative commons and works for this example:

This is an image of the libraries logo

To make an ImageSheet, we need to open the properties for our application, which can be done by opening the context menu (right click on the project or navigate to the projects window (CTRL+1), select the project, hit the context menu button), then select "properties". From there, we navigate using the keyboard or mouse to the Games window on the left pane, which looks like this:

This is an image of the image sheet dialog

On the left pane of this window, there are two categories of options, Project Information and Games. If Games is selected, the right hand side contains information that tells Quorum to automatically generate ImageSheets. There are several options, which are listed below:

For our example, we will fill out our ImageSheet to include both the books image and the hour of code image. Our window will have a build path of "assets" (although it can be anything we want), with one ImageSheet, which we will call "MyImageSheet", and two files in that sheet. Our window appears like this:

This is an image of the image sheet dialog full

Once the ImageSheet generator is enabled, Quorum will create them on each build (each time we hit Run) if that option was selected. When this is done, Quorum creates two files, MyImageSheet.png and MyImageSheet.atlas. First, MyImageSheet.png combines both images onto one (or several if they will not fit on one) image that looks like this:

This is an image of the libraries logoThis is an image of the Hour of code Quorum logoBesides the image, a second file is also generated, called an atlas file. This file is generated by the excellent tool LibGDX. Normally, we do not need to interact with this file directly, but the engine needs to know about it. In other words, barring a user has specialized needs for a particular application, we can ignore the contents of this file. The file is needed in order to let Quorum know where each sub-image is in the larger ImageSheet. For reference, here is what the file looks like in this example:
MyImageSheet.png
size: 512,256
format: RGBA8888
filter: Nearest,Nearest
repeat: none
books
  rotate: false
  xy: 2, 32
  size: 200, 130
  orig: 200, 130
  offset: 0, 0
  index: -1
hourofcode
  rotate: false
  xy: 204, 2
  size: 160, 160
  orig: 160, 160
  offset: 0, 0
  index: -1

Once our ImageSheets are generated, we can then use them in a program, similarly to how we used Drawable's before. First, we add a use statement for ImageSheets:

use Libraries.Game.Graphics.ImageSheet

Next, where we create our Drawables (books and bunny), we also create an ImageSheet from which we will extract the Drawables.

class Main is Game
    Drawable books
    Drawable bunny
    ImageSheet sheet

Then in the CreateGame action, we load the files from the ImageSheet into the Drawables and Add them to the game:

action CreateGame
    sheet:Load("assets/MyImageSheet.atlas")
    books = sheet:GetDrawable("books")
    bunny = sheet:GetDrawable("hourofcode")
    Add(books)
    Add(bunny)
end

In this example, these images will draw over each other because we have not changed their position, but you can see how we can now load multiple Drawable objects onto the screen. We explain how to position objects in the next tutorial.

Here's what our final code looks like for loading two images from an ImageSheet and displaying them on the screen:

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable
use Libraries.Game.Graphics.ImageSheet

class Main is Game
    //store a placeholder in memory for books and the bunny
    //by saying these are undefined, we are telling Quorum not to make the objects
    //the reason we do that is because the ImageSheet will make the objects for us
    Drawable books = undefined
    Drawable bunny = undefined

    //create an ImageSheet object we can use in our create game action.
    ImageSheet sheet

    action Main
        StartGame()
    end

    action CreateGame
        //this loads the atlas file into the ImageSheet. Importantly, we load
        //the .atlas, not the .png. The reason is because our atlas generator may 
        //generate multiple images, which means the system should load them all.
        sheet:Load("assets/MyImageSheet.atlas")

        //load the two Drawables from the ImageSheet.
        books = sheet:GetDrawable("books")
        bunny = sheet:GetDrawable("hourofcode")

        //add the Drawables to the game.
        Add(books)
        Add(bunny)
    end

    action Update(number seconds)
    end
end

Here's what this code will render:

This is an image of the Image Sheet Result

Next Tutorial

In the next tutorial, we will discuss Animation in 2D, which describes how to use animation in 2D.