A blog about my rantings, including Games, Game Development, Gaming, Consoles, PC Gaming, Role Playing Games, People, Gaming tips & cheats, Game Programming and a plethora of other stuff.
I've literally been inundated by a message about my XVE project, specifically calling out why I have the temerity to describe it as an "Engine when it looks do basic?"
My over simplification in reply being that an engine can be anything you please, the engine is simply the framework within which the content can execute.
This applies to Unreal, Unity, Godot all of them, if you have something which will load your models, compile/apply your shaders and render to the screen you have a graphics engine. If you have a threading model, memory or allocation handling, logging, input, user interface... All these module pieces form an engine.
The reason I am investing time working in my own such project is to explore those modules in which I find my experience or knowledge needing expanding, where I wish to trial alternative approaches, keep up with emergent new techniques or processes.
And of course for the content itself to explore the content creation tools; for instance I am very much a Blender beginner, and learning fast. I could not say this just three months ago.
The major advantage to me in performing this kind of exploratory work at home in my own time is of course that I take a confidence and an assuring confidence back to my real work, as a leader of a small team I feel I am equipped to jump into pair program and just help. I feel equipped going over a diverse or new code base; for instance recently I explored the Dagor engine used by WarThunder and the Little Vulkan Engine by Brendan Gaela.
Chronologically XVE has gone over many internal milestones, long before I began posting anything to YouTube or this blog about it, including:
CMake & Project Structure
Coding Standards
Git & Commit Hook integration for Jenkins Continuous Integration Builds
Test Framework
Module Design
Base
Renderer
Data loading
XML
CSV
Wavefront Obj
Generated type definitions (a whole generator suite written in python
Entity Component System
Threading Model
Thread pool
Anonymous Async Dispatch
Promise/Future Standard Exploration
Memory/Allocator Models
Slab Allocators
Dynamic Allocators
RPMalloc
Standard Allocator API (Custom)
Input Framework
XInput/DirectInput
SDL2 Input
SDL3 Input
GLFW Input
Custom personal exploration
And only after all this did I begin exploring actual game content and systems and component and entity relationships to represent the game design concepts.
The engine becomes a tool, a tool to achieve the delivery of the experience we want our players to experience and enjoy, so they return time & again to our game.
The game is key, and if done right the player should never know what engine or framework is being used to deliver that experience to them.
For about two years I've had a home game engine project, this has just been a little sandbox in which I have been able to experiment with graphics API's, systems, memory, threading models and other such "Game Engine" like things; it has simply been a learning aid to keep myself invigorated.
You see I work on a AAA game engine, for a game yet to be released and that engine does a lot of things its way, tens of engineers work on it in the "Engine" space.
I therefore wanted a space of my very own in which I could explore, the key piece I have done which ape that real engine have been using an Entity Component System.
My Ecs is quite different from the one at work, my focus is on game objects being made of many entities and those entities carrying discrete components to add their functionality. A very data driven ideal.
A great example might be simply putting a model on screen, lets take a complex object. The Missile Launcher as a case study and we will be looking at the code before images from the running application.
To place the game object entity I create the Object structure, filling it out and assign it a transform and launcher state instance, these components give the engine a key into the nature of what this entity is. There are many explanations of Ecs out there, but simply put we get an entity handle (an Id) and assign to it certain component types. The presence of these components then tell us everything we want to know about the entity:
components::Object launcherObject
{
.mId =GetNextGlobalObjectId(),
.mDebugName ="Launcher"
};
components::LauncherState state;
components::Transform3d launcherTransform
{
.mPosition = { 4, 1.5, 4 },
.mRotation = { 0, 0, 0},
.mScale = { 1, 1, 1 }
};
This could be all we need for the entity, indeed this is a design decision I am yet to fully explore. However, in the actual code I immediately want the base model for the renderable to have this same transform.
Adding this is as simple as assigning more components:
components::RenderablebaseRenderable
{
.mMesh=GetMeshIndexForName("Launcher_Base"),
.mTextures { GetTextureIndex("swatch.png") },
.mVisible=true,
.mBlended=false
};
components::Shadershader
{
.mShader=GetShaderIndex("Brambling1"),
.mWireframe=false,
.mLighting=true,
.mFog=false,
.mCameraRequried=false
};
The renderable and shader component are very strong indicators of quite how this works, an entity without these components will not ever draw.
In fact there are three components needed for the "Draw" system to pick up the entity and run the graphics API calls over the entity, these are the Transform, the Renderable and the Shader components; these three together indicate that this is something the engine will draw.
And the system simply iterates all the entities in this view and we have just those entities to show.
(At this point I know a bunch of you are lost, but lets just think of this like we've divided the thousands of entities in our game such that only those with these three entities will appear in our View).
We have right now though just got the object itself showing and the model showing is only the base, I want to rotate this base.... I have a component for that:
components::ManualRotationYmanualRotationControl;
Assigning this component to the entity and the functionality this component brings is assigned to our launcher!
This is the key power of an Entity Component System, we can author discrete and well defined features as a component and which operate on the member data within that component and then assign it to any other entity we desire. Manually Rotating the entity around the Y (or up axis) is a good example.
Another might be the speed of a vehicle.
We however want to add a body model to our launcher object, our second model looks like this:
Adding a child entity to this launcher object we can keep all the components separate for the body than the base:
components::Transform3dbodyTransform
{
.mPosition= { 0, 2, 0 },
.mRotation= { 0, 0, 0},
.mScale= { 1, 1, 1 }
};
components::RenderablebodyRenderable
{
.mMesh=GetMeshIndexForName("Launcher_Body"),
.mTextures { GetTextureIndex("swatch.png") },
.mVisible=true,
.mBlended=false
};
components::ShaderbodyShader
{
.mShader=GetShaderIndex("Brambling1"),
.mWireframe=false,
.mLighting=true,
.mFog=false,
.mCameraRequried=false
};
components::ManualRotationXtiltControl;
constautobody{ CreateEntityWithParent(launcher,
std::move(bodyTransform),
std::move(bodyRenderable),
std::move(tiltControl),
std::move(bodyShader)) };
Because we have added this second renderable as a child (the parent being the launcher itself) the transform is hierarchical and the launcher now looks like this:
We want the launcher to carry missiles, but where to place them? Well, I have invented a "socket" system, that when you place a socket entity onto something you can find the socket and insert something into it...
std::array<glm::vec3, 4>missilePositions
{
glm::vec3{ -1.2, 0.5, 0.5 },
glm::vec3{ -1.2, 1.0, 0.5 },
glm::vec3{ 1.2, 0.5, 0.5 },
glm::vec3{ 1.2, 1.0, 0.5 }
};
for (uint32_tindex{ 0 }; index<missilePositions.size(); ++index)
We can see that for each launcher in the Launcher View (which is just any object with a components::LauncherState) and we know we can pull out that component and work on adding a missile if the launcher timeout has passed the reload time; calling AddMissileToSocket you can imagine looks for children of the launcher which have the components::LauncherSocket component type, and voila we have the transform from the base, the body and the socket to place the missile.
Putting all this together and we get:
You can see the component have their own UI (ImGui) controls, that one has complete control over the whole entity and each individual component.
And the parent-child relationship between different entities within the object allow for really complex game objects to be described in fairly quick turn around times.
Another example of this is my test vehicle, which has a rolling wheel to match the movement; the movement belongs to the vehicle, which is a game object with a renderable. But the wheel itself is a child, it has a renderable too, but also its own transform and its own "Wheel" component, which has the discrete maths for the circumference and rotation.
The effect of rotating the wheel per frame is even its own other component, so that we can assign the same "Rotate per frame" component to anything in our scene.
With all this technology in place, the basic rendering engine as you can see (in OpenGL 4.3) I am now placed to expand the amount of content and start to build an actual game.
You see a game engine, or any engine, can only really fulfill its potential and for me complete the learning round trip by creating and driving something, for me this will be a game.
The missile launcher is perhaps a hint as to the kind of game I am picturing. but the models I am working on as of new years night are perhaps stronger clues:
The Entity Component System has been a key implementation for my to progress onto content creation and, besides a few performance fears when I scale up the combat scenes I hope to deliver, I believe it to be the best choice for fast and easy iteration on a theme.
The mental leap of a game object being one or many entities, each with their own features dividing up the mammoth task of implementing all the features of a game for myself as a solo developer has been absolutely key.