Article

Game Engine Project Part 1: Creating a Game Engine

Author: Tony

Date: May 16, 2023

A game engine was created over four months using C++ as part of my Final Year University project. This project looked at using Artificial Intelligence to improve users’ engagement with the game. This section will summarise how I made the Game Engine, Level Editor and finally the Game. To start with, the Game Engine is subdivided into several module which all work together to produce a level editor and a game. I will briefly describe in the following sections how each module works within the game engine.

Game Loop

The Engine has an update loop which processes input/outputs and manages the resources being used and renders to the display for the user. This update loop is maintained until the program is terminated.

void Application::loop() {
gameClock->updateTime();
    //FPS variables
    float timePerSec = 0;
    int loopCount = 0;
    float deltaTime = 0;
    while (running &&  !glfwWindowShouldClose(gameWindow->getWindow())) {
        eventSystem.handleEvents(Type::keyPressed);
        eventSystem.handleEvents(Type::keyReleased);
        glfwPollEvents();
        auto scene = sceneManager->getCurrentScene();
        
        //TEXTURE SELECTION UPDATES
        scene->setPicking();
        frameBuffer->bind();
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        //SCENE UPDATES
        scene->update(deltaTime);
        frameBuffer->unBind();
        //IMGUI UPDATES
        imguiWindow->update(deltaTime, scene);
        glfwSwapBuffers(gameWindow->getWindow());       
        
        //GAME CLOCK UPDATE
        gameClock->updateTime();
        deltaTime = gameClock->getTime();
        timePerSec += deltaTime;
        loopCount++;	
        if (timePerSec > 1) {				
            timePerSec = 0;
            loopCount = 0;
        }
        gameClock->endFrame();
        gameClock->updateTime();
    }
    glfwDestroyWindow(gameWindow->getWindow());
    glfwTerminate();
    glfwSetErrorCallback(NULL);
}


Scene Manager

The next major part of the system is the Scene Manager which was created to allow for switching between different scenes. The scene manager contains a dictionary of Scene classes and stores the ID of the current scene allowing for a switch between scenes by changing that ID. The scene class was decided as the central point of the Game Engine as it contains all the information that could be found within that scene. The render, animation and controller managers are stored within this class for accessibility alongside the game window.


Render Manager

The Render Manager consists of a Renderer class that store a Vector of RenderBatch objects. Each batch stores the vertices and textures which is used by OpenGL to send to fragment and vertex shaders on the graphics card. A Z-index system was also added to the Render Manager where a batch can be assigned an order in which to render, this allows the game engine to display graphics on different layers. The renderer has also been designed to check when batches are full and to keep track of which batches have empty slots that vertices can fill.


Entity Manager

The Entity Component Manager manages the entities used within the Game Engine. An example of an entity would be a player, enemy or wall. My choice of design for this System was influenced by 2020 article "Application of Data-Oriented Design in Game Development" which suggested that using a data-oriented approach can lead to improved performance. Another disadvantage of using Object Oriented is the deadly diamond, where a class can inherit from multiple classes which inherit from the same parent class. For an entity this could be a frequent occurrence because a game may require an entity to have different customised attributes.

The final design approach I took for this system was a Data Oriented Design using a hybrid approach of the well-known Entity Component System (ECS) without the System. Each entity would be broken down into separate components and all components would be stored in different component lists. Each entity would have an index and that index would be used to access the different components lists. An example of components used within my Game Engine would be a Health, Sprite and Physics Collider components.

There is however a major issue with this type of design, because of the large amount of memory being allocated a customised memory allocator needed to be created. The memory for each list is pre-allocated using a pool allocator, each components list has a pool of memory that is specified before allocating resources. The pool allocator itself is created using a linked list that iterates through a list of pointers to the next memory address.


Event Manager

The event system passes messages between the classes of the Game Engine. The event system is built using a publish and subscription design, where a function within another class can subscribe to an event by passing a function pointer to the event handler. The event handler will store the function pointer with a wrapper class alongside the event to be called. A class can then publish an event and the event handler will ask the wrapper class to call the function with the details of the event. All the events are stored in a dictionary alongside an ID to allow for event sorting. The event manager can publish an event immediately or be handled at a later point in time.


Resource Manager

The resource manager stores the Sprite Sheets, Textures and Shaders, each stored in a dictionary containing both a unique identifier and the resource. The resource manager was set as a global variable to the Engine, that means that any class can access it across the game engine, this was done to reduce the number of references being passed between classes for access to the resource manager.


Animation Manager

The animation manager retrieves data from a JSON file containing keywords and frames associated with those keywords. The Animation Manager class stores an animation list, which is a dictionary using a unique identifier to store the Sprite identification for a Sprite Sheet and the amount of time to display that sprite. Each update of the engine goes through a list of Entities with Animation Components and updates that Entities animation using the animation list.

This is used in my game to show collision impacts from enemy attacks and to display the state of health of both enemy and player.


Controller Manager

A Controller class is important to the Engine as it receives the inputs from either the User or an AI and maps that input to different actions. The Controller in my Engine uses the Command design pattern. This design was chosen because of the simplicity of changing the Player Controller to an Enemy Controller and the possible future extension of remapping actions and adding different Controllers. The Client Class receives a Command object after calling the action function in the Receiver class. The Receiver sorts which Command is to be returned and the Client calls the execute function on the Command after passing the Controller as an argument. The choice of design pattern was also chosen for the enemy controller as it makes it simple for the AI to create its own command objects and act as a receiver.


The Physics Manager

Physics was implemented using a pre-existing physics engine called Box2D. This engine runs on a separate thread from the game engine and simulates a world based on information given to it. A physics class was created to store this information where each entity with physics components would be stored as a list of physics bodies in the physics class. Each frame update, the engine would update all collisions and positions. Custom call back methods were created for collision contacts and ray casting. A pointer to information storing the type of entity is passed to Box2D to determine which entities are colliding and the call back functions are used determine the logic of the two entities in the collision.


Level Editor

A level editor was also created using Dear ImGui which is a library which makes it simpler to create user interfaces. The level editor has a drag and drop function which allows you to create entities in the level and move them around.

The drag-and-drop function overall made it easier to create and manage a level scene and reduced the amount of time it took to make a large maze-like level for this project. As shown below each player, enemy and wall is a placeable block within the level.

Dragging and dropping an entity in a grid alignment has also been added for quality of life purposes.

Functions for copying and deleting blocks were also added as quality of life features for building a level, in the level editor these are represented by buttons and are also mapped to certain keyboard keys.

Properties for individual entities could also be changed by clicking on an entity. The different components of that entity could then be altered. Shown below a block can be selected and its Sprite colour can then be altered. There are also different collider components that can be changed to change the logic of gameplay. An example of this could be a player being set to not collide with a wall entity.


Game World

The game world was created using the previously mentioned level editor. A tile map of the level floor was drawn using the Aseprite software. This tile map is set to be rendered once and left as a static image in the background. Animations and character designs were also created using this software and given to the engines Animation Manager. Each entity was placed using the editor and saved as a JSON file. The level was designed to be separated by grid squares to reduce complexity and a maze-like wall structure was created to complement the AI. A level end point was set to give the user a goal to get to. A birds-eye view of the world was chosen because it can show the AI’s range of actions very quickly in a limited amount of time.

As this post has become lengthy a separate post will be made to showcase the Artificial Intelligence and the outcome of using Goal Oriented Action Planning for the enemy Agents.

If you are interested in the code for my game engine, then you can find the code here on Github!

I would finally like to make mention of Jason Gregory’s book called Game Engine Architecture which was used constantly as my main reference for making this game engine. The book can be found here!