Real Engine

RealEngine is a modern C++20 engine developed over approximately 12 weeks. I am enthusiastic about continuing to work on and improve it. The engine is built using various SDL libraries and GLM. Multiple design patterns from the book Game Programming Patterns by Robert Nystrom were employed to make the engine more manageable and easier to use. Although we can't be certain if the engine functions without an application built on it, I have recreated the arcade classic Pengo to demonstrate its capabilities. This game also incorporates several patterns, such as the state pattern.

I thoroughly enjoyed working on this project and delving into the more low-level and technical aspects of programming. I learned a great deal, especially about smart pointers, template programming, and some STL algorithms.

View on Github
Project 1 Image

About Real Engine

Introduction

The library is split up in multiple sub libraries. There is RealCore, the base of the library, containing your sound, input, scene logic, game object and component logic and the transform component, which is in charge of everything position, rotation and scale wise. Then there is the Real2D and Real3D parts, each in charge of rendering, the 2D engine uses SDL and a SDL renderer, while the 3D engine uses Vulkan for rendering and SDL for the window. Both have unique components.

Real Core

As mentioned before, RealCore contains everything shared between Real2D and Real3D. It also employs numerous patterns, such as the component pattern, command pattern, service locator, dirty flag, and more. We will discuss these later. Let's look at some key aspects of RealCore:
     The InputManager class handles all input. The user first creates an input map, which they fill with actions. These actions contain an input type (controller, mouse, or keyboard), the input key, and key state (up, down, or pressed). These parameters are then linked to a custom command derived from either the GameObjectCommand class or the Command class.
     The Locator class currently only contains an audio service. Here, the user can control everything related to audio. When a sound needs to be loaded, the class pushes an event onto a queue. When the time comes to play the sound, it loads and plays the sound on a different thread for maximum efficiency.
     The GameObject class is the core component. This class can be filled with individual components, either engine-defined or user-defined. There are two types: the DrawableComponent class, with a render function, and the regular Component class. Game objects can also be bound to other game objects through a parent-child relationship. They are bound to the scene by default.
     Scenes are managed through the SceneManager class, where scenes can be created, loaded, and exited. A specific input map can also be bound to a scene.
RealCore also includes an Observer and Subject class, a GameTime class, a Logger class, and various other utilities.

Real 2D

Real2D, as the name implies, is a 2D engine that uses SDL to render and create the window. As previously mentioned, it is built upon RealCore. Its engine class derives from the BaseEngine class located in RealCore. The engine comes with several components, including the ColliderComponent, which handles collision; TextComponent, which renders text to the screen; SpriteComponent, which renders animated sprites; and TextureComponent, which renders still textures. The TextComponent also requires the TextureComponent to render its text. Fonts, textures, and sprites can be loaded through the ResourceManager. The 2D renderer also supports ImGui.

Real 3D

As the name implies, this is a 3D engine. I won't go into too much detail here, as you can find all about it on the following page here. In short, the engine is a Vulkan abstraction, and the window is again generated by SDL. It comes with several handy components and its own ContentManager class. This time, the project is created with CMake.

Patterns Used

You probably already know that this engine consists of multiple patterns. Let's go over the most important ones.

    The Observer pattern is used extensively throughout the engine. A small example is in the SceneManager class, where it notifies all listeners when a scene gets loaded or exited. The subject can pass multiple arguments through its notify function that notifies the observer.   If you want to learn more about this pattern, you can read about it here.

    Another pattern used is the Command pattern. The engine uses it in combination with input. In RealEngine, there are two variants: GameObjectCommand, which has a GameObject* parameter, and the regular Command. When the correct combination of input key, type, and state have been met, the InputManager will call the Execute() function of the command, which will execute the user-defined logic.
  If you want to learn more about this pattern, you can read about it here.

    The most important pattern of them all is the Component pattern. This pattern follows the rule of composition over inheritance. It decouples the game objects significantly. Instead of having each game object derive from a class with some logic, a component can be added through, let's say, GameObject::AddComponent<T>(), along with some arguments. The component has some virtual functions like update, render, start, kill, and so on. Plus, it has a reference to its owner, so it can call other components of said owner.
  If you want to learn more about this pattern, you can read about it here.

There are a few more patterns that need to be mentioned, such as the service locator, the singleton, the dirty flag, the game loop, of course, and the event queue.

Future Work

I would love to keep working on this project. It has taught me a lot about template programming, smart pointers, and writing cleaner and more manageable code. Especially in the 3D part, there is much room for improvement. A level editor could also be a future project of mine. Only time will tell.

Pengo Game

Pengo

As mentioned before, a fully playable clone of the arcade classic Pengo is implemented with the engine to demonstrate its functionality. The game is almost a complete replica, with improvised enemies, some animations, and minor adjustments to settings. The game features 3 modes: the regular PvE mode, multiplayer mode where players can cooperate to defeat Sno-Bees, or compete against each other with one player as Pengo and the other as a Sno-Bee.
     The levels are procedurally generated using a reversed backtracking algorithm. High scores are also locally saved, allowing players to track their rankings on their own machine.
     One design pattern used in this project is the state pattern. Enemy behavior is implemented using multiple states, and the playable enemy also utilizes these states.

This project has taught me a great deal about gameplay programming. I emphasized keeping classes small and manageable, utilizing the state pattern to avoid a monolithic enemy class that handles everything. Similarly, a shared movement class reduced duplicated code across multiple classes, and splitting different functionalities into separate classes prevented a large, unwieldy player class.