
Velox
2022-2023
Introduction
Velox is a 2D game engine I decided to build to learn about different aspects of a game engine while also growing my skills in C++. Using my own engine to develop games has always been a dream of mine.
​
The engine has its foundation in SFML, with the goal of ultimately providing a variety of functionalities for expanded usage. Some functions of the engine are also based on the book “SFML Game Development (2013)” by Jan Haller, Henrik Vogelius Hansson, and Artur Moreira.
​
You can find all the code in the GitHub link and going to the \vlx folder. Any questions or insights can be emailed to me at tobias.garpenhall@gmail.com. I'm very much open to any constructive criticism as this is my first time building anything of this scale.
Description
The goal of the development is to create a game engine that contains a variety of functionalities that makes the development of a game a lot easier and smoother.
​
I specifically choose to develop a 2D game engine because I do not want to burn out early and also have a stronger focus on learning the basics of the essentials modules of an engine. Developing a useful 3D engine requires years of effort from many experienced developers, and I feel that I should instead focus on 2D, which at least helps yield a solid foundation of knowledge for the future. The engine furthermore won't feature any kind of runtime editing, and will instead function more like a library. For example, the user only has to include “Velox.hpp” and may then create a World object that initializes all essential objects (window, input, texture holder, etc.) and also updates all the stored systems accordingly in a loop.
​
While the engine is designed mostly for me to learn some modules, I am also hoping that when near completion, it may be useful for anyone else who wishes to develop a 2D game.​
ECS
The ECS is a design paradigm for describing the relationship between entities, components, and systems. Entities are represented with just a simple ID integer, components contain plain-old-data, and the system acts on entities with a specific set of components. This approach solves many issues related to inheritance, such as diamond problem and the design just becoming too complex. The ECS can also be made to be data-oriented by storing the components' data close to each other in the memory and thus improving cache-friendliness (not guaranteeing it, of course). The ECS may improve performance a lot of the time, however, it has to be properly used, for example, components should generally always be very small in byte size. As always, there is no silver bullet to solve every problem, I just believe ECS is a very powerful tool to have.
​
The ECS is a core part of the Velox game engine and will be the first module to be developed. Many of the future modules will use the ECS as a foundation, such as the physics system. Currently, the ECS is pretty much complete and ready to use. Here is a list of things you can do using the ECS in its current state:
​
-
Add or remove an entity
-
Add, remove, or retrieve a component from an entity
-
Check if entity has a certain component
-
Store component references that always remain valid until deleted
-
Retrieve entities that hold a certain set of components
-
Duplicate an entity
-
Sort components as determined by the user​
-
Define a system that runs for every specified component
-
Exclude entities that hold any specific components
-
Specify the layer the system is located in (when it should run)
-
-
Register to events that runs when components are added, removed, or moved
-
And some smaller convenience features here and there
​
The code excerpts below are an example for how a system is run by finding the archetypes that contains the components and then linearly iterate through each component with the goal of improving cache efficiency.
Physics
Physics was the next module that was worked on, which applies the ECS into practice. The idea is that all the user has to do to make physics work is to add a PhysicsBody component to an entity (as well as a collider and shape). The ECS and the physics system will take care of the rest to make sure the body is added, updated, and removed correctly.
​
As for the collision system itself, it is divided into what is commonly known as a broad phase and narrow phase. However, first, the user has to add a collider and a collider shape for the entity, for example, circle, box, or convex. Also, a PhysicsBody is actually only required for the entity to be affected by physics. Entities that have a collider and shape can collide just fine, it just means that only events will be called when first entered, overlap, and on exit, which the user can register to listen for.
The broad phase will detect when any of the essential components are added and will automatically create a body for the entity to store references to such data. Furthermore, before the data is ready to be used, it is also inserted into a quad tree for faster nearby search. When the physics system is first updated, the broad phase will collect all potential collisions pairs by checking if each entity AABB collides with another nearby entity. Afterward, all the collision pairs will be forwarded to the narrow phase that uses a collision detection algorithm for every case, for example, box against circle, or convex against convex. The algorithm will also output data such as penetration, normal, point of contact, and more. If both entities have physics bodies, they will be added to another list called arbiters, where they will be later forwarded to the physics system for collision resolution.
The implementation of the physics calculations is generally done similar to Box2D, albeit a lot simpler. The physics system is therefore not that unique, for example, it integrates the positions and velocities in a fixed time step, collision resolution is impulse-based, and bodies may sleep after having been inactive for a time. Further work would be to investigate issues relating to body stacking jittering, check out joint physics, and implement more shapes (lines, capsule, or triangle).
​
The code excerpt below is just an example for how collision is resolved using impulse resolution.