GTCS Game Engine:
Tutorial 1: Basic Application Structure

Tutorial 0 <-- Tutorial 1 --> Tutorial 2
Tutorial Homepage


Introduction

In this tutorial, we will look at the basic building blocks and programming structure to build games. We look at the different types of objects we use and their respective roles with games. We will also gain an understanding of the operation of our game loop.

Covered Topics: Object TypesScene ObjectInitializationGame Loop

Demonstrations: Drawing a RenderableRenderable with Motion

Complete source code for all tutorials can be downloaded from here.


Object Types

To begin with the GTCS Game Engine, we are concerned with four types of objects: Scene, Camera, Renderables and GameObjects. Each object type encapsulate different functionality.

We will look at each element by building a simple game scene to create a simple rectangle on our viewport as shown here.


Scene Object Structure

The scene is where you define what happens in your game. By overriding the engine's Scene class with the functionality you want, you will be able to add your game elements to the existing framework and create multiple game levels that . By convention, this class will be defined in a folder located within the src level, in Tutorial 0 this folder was referred to as your_game_folder.

The HTML5 engine implements a runloop. When you setup a scene object, you must adhere to a certain structure to make sure the runloop executes as expected. The structure of a scene currently consists of five parts: loading, initialization, drawing, updating, and moving to the next scene. A skeleton of the functions in a scene object are shown in the following code...


class your_game_scene_type extends engine.Scene {
        constructor() {
            super();
            this.mCamera = null;
        }

    init() {
        this.mCamera = new engine.Camera(
            vec2.fromValues(50, 50),  // position of the camera
            100,                       // width of camera
            [0, 0, 600, 600],         // viewport (orgX, orgY, width, height)
            2                        // viewport boundary
        );
    }

    draw() {
        engine.clearCanvas([0.9, 0.9, 0.9, 1.0]); // canvas color
        this.mCamera.setViewAndCameraMatrix();
    }

    update() {}

    next() {
        super.next();
    }
}
window.onload = function () {
    engine.init("GLCanvas");
    let game = new your_game_scene_type();
    game.start();
}
Code snippet 1-1: Scene subclass skeleton

Note: The highlighted portions are specific to your game implementation.

Your scene subclass needs to inherit from the existing Scene object type. You must make sure that the name of this JavaScript file matches the path indicated by your index.html file from Tutorial 0.


Initialization

onload & Constructor

Before any game logic or rendering occurs, initialization occurs in three phases. First the window's onload() function is called, creating a new instance of your_game_scene_type. Then, in the constructor instance variables of objects are declared, but set to null. In the skeleton snippet above an instance variable for the scene's camera is declared. Once resources are introduced in Tutorial 2 the paths to resource files are declared in the constructor as well.

init()

The init() function is called by the engine to setup your various objects. In this example, we create our remaining core objects with simple values to aide in comprehension.

    init(){        
        this.mCamera = new engine.Camera(
                vec2.fromValues(50, 50),  // position of the camera
                100,                       // width of camera
                [0, 0, 600, 600],         // viewport (orgX, orgY, width, height)
                2                        // viewport boundary
        );   
        this.mRenderableBox = new engine.Renderable();
        this.mRenderableBox.setColor([1.0,0,0,1.0]);
            
        this.mGameObj = new engine.GameObject(this.mRenderableBox);
        this.mGameObj.getXform().setSize(10,10);
        this.mGameObj.getXform().setPosition(70,70);
    }
Code snippet 1-2: Scene init()

Camera and Viewport

The Camera object defines the viewport and is setup with 3 parameters. The position, the width and the location/size of the viewport.

[Note: The camera height is not provided. The engine infers the height based on the width and the aspect ratio of the viewport.]


Renderable

The Renderable controls the "look" of objects in our game. The basic one we create in the above example renders only a solid square. We set its color to red using a "color" which is defined by a RGB+Alpha array. We cannot make interesting games if our renderables are limited to making solid rectangles. In future tutorials, we will look at more advanced renderables that can draw textures, animate and respond to lighting effects. The key take away in this tutorial is that a renderable is just for defining the appearance of the object.

this.mRenderable = new Renderable();
this.mRenderable.setColor([1.0, 0.0, 0.0, 1.0]);

These lines will allocate a new renderable object and set it's look (in this case, color). The parameters for setColor() is a vector with R,G,B,Alpha values ranging from 0.0 to 1.0.


GameObject

The GameObject is created with a reference to our renderable. We give the GameObject it's size and location in the viewport. Once the renderable is fully configured and incorporated into our GameObject, we will keep the reference in case we need to access it directly to change the appearance. Often, you will subclass GameObject to encapsulate it's renderable upon allocation.

this.mGameObj = new GameObject(this.mRenderableBox);

The size and position are in WC space with the position being the center of the object. In this example, since our size is 20x20 and position is (70,70), this will set our red rectangle from (60,60) at the lower-left corner to (80,80) at the upper-right corner.

this.mGameObj.getXform().setSize(20, 20);
this.mGameObj.getXform().setPosition(70, 70);

[Note: understanding different coordinate spaces is beyond the scope of these tutorials]


Game Loop

Updating

The update() function is called by the engine 60 times a second. This is where you will check for user interaction (i.e. keyboard presses and mouse clicks), check for collisions among GameObjects, update object locations etc. If another process or calculation delays execution, the game engine will compensate for the delay by looking at the current time and executing the update routine multiple times until updates have caught up with current time.

    update() {
        let xform = this.mGameObj.getXform();
        if(xform.getXPos()<10){
            xform.setXPos(70);
        }else{
            xform.incXPosBy(-0.5);
        }
    }
Code snippet 1-3: Update

In this example, we want our GameObject to move left across our game screen and then jump back to its starting position. We have an if statements that determine when the GameObject hits the left edge. Note that we directly manipulate the transformation of the GameObject, decreasing its X position each update.

Drawing

The draw() function is called after the update cycle is complete. On an unencumbered system, this should be 60 times a second. If there are delays, multiple updates can occur before the draw routine is called.

    draw() {    
        engine.clearCanvas([0.9, 0.9, 0.9, 1.0]); 
        this.mCamera.setViewAndCameraMatrix();
        this.mGameObj.draw(this.mCamera)
    }
Code snippet 1-4: Draw

In our draw function, we clear the screen, activate our camera for rendering and draw all of our objects (which is only one at this stage). We send a reference to our camera into the game object which will pull out the information it needs for behavior and send the camera to the enclosed renderable so that it can extract what it needs for drawing. Click here to see the results of drawing and updating the GameObject.

When viewing this in a web browser, you should see the following...

Figure 1-1: GameObject with Renderable

You will also notice that the coloring is darker than you were probably expecting. Our renderable object responds to a "virtual light source". By default, our only light source is ambient lighting and it is very dim so as not to interfere with other light sources you may define. We will look at adjusting ambient lighting in the next tutorial.


Conclusion

We have learned about the structure of the scene object and create a very basic scene with a single renderable.

In Tutorial 2, we will look at accepting user input and utilizing game resources such as bitmap images and audio. We will build on the renderable concept and look at new types of renderable objects that support bitmap textures.


Tutorial 0 <-- Tutorial 1 --> Tutorial 2

Tutorial Homepage

5/13/2022 - By Myles Dalton