Dragonbones Engine
The Dragonbones Engine is a C# based game engine, built from scratch by myself. The three pillars of the engine's design are as follows:
- Modular
- Multithreaded
- Extensibility
Modularity means that the engine is built so that every component can be replaced including core elements of the engine. The core of the engine is entirely on class interfaces so they can be replaced with differing implementation. This is done so that elements can be redefined to better suit the game. While the engine does include default implementations for all of the elements they can be replaced with little damage to the overall system.
Multithreading is very common in game engines, and while this isn't unique it is important to know that it is a priority in the Engine. By default the engine will attempt to start a thread for each logical core in your machine and then use them to load balance the processes. This also has led to certain design decisions which allow for the developers using the engine to be able to gain the advantage of multithreading without having to worry about managing the syncronization themselves.
Extensibility is not uncommon in game engines either but building it into a core idea of the engine means that games can easily handle adding mods or expansions. Even the games themselves use the same system to begin running just without a parent, unless it is being loaded in an editor. The plan is to require a secret contained in a file from development to have full access in editor to a game, otherwise the game only provides guarded access to the editor. Similarly, Each mod is silo'd from the game, but the game normally will be given full access to the mod and the mod will be able to request access from the game.
Design
The Dragonbones Engine architecture is based on Entity-Component-System(ECS) architecture. There are currently five major objects in the engine, Components, Systems, System Schedules, Entities, and Text. Entities, Components, and Systems are very similar to base ECS, but System Schedules and Text are additions.
Text is handled at a high level to allow for all parts of the engine to rely on a common system for text, which also includes entity or component names. System Schedules are an abstraction to handle how systems are multithreaded. Each schedule runs independently, and within a schedule systems are grouped based on order and whether they can run together or not.
There are two other major differences to standard ECS. The first is that everything is split into read and write variants. This allows for some optimization in system schedules which are read only (like render systems) as system run order is less constrained. While read isn't 100% read only due to reference objects, but those are expected to handle synchronization themselves. The second difference is that unlike traditional ECS, everything is a component or held by one. This means that the entire engine uses a standard system to store most data. Entities for instance are stored in EntityStores which are treated as engine-wide components. Text is similar but in TextHeaps. This methodology has led to components being far more important than entities and several systems (which are stored as components) use components directly without using entities. Entities are simply used when grouping several components is important. Since most data is stored as components they all have both a text name and an numeric id to look them up by.
Plans and Progress
Currently the Engine has its Heart or core systems built. It can start a "game" from a dll and run system schedules to process data. I have begun to create several base rendering systems and can render basic triangles, splines, and textures. The main stumbling block right now is that the engine does not know how to read texture or font data from disk. I am also working through a refactor to have better access patterns and better handle text data.
Once the basic rendering systems are complete. I will focus on adding input systems. Once that is finished I will start building systems to hold level data and begin building a GUI for the engine. The Engine will then be built around a metroidvania project to keep the work directed.
History
This project's history is rather interesting as I have worked on this project in one form or another since about 2010. Now that being stated, the project looks nothing like what it did back in 2010. I started this project originally to back a single set of games I had an idea for, the Ioran Chronicles. Those games, while something I still want to produce, also look nothing like what they did when I started and even the name Ioran Chronicles was not the original title for the series. The original idea was to build a 3D adventure game which would help me to learn a lot about 3D and then build an MMORPG with the world I worked on. That idea now seems utterly ridiculous as the scale of an MMORPG is not really a second project sort of thing, which even early on I learned quickly. As I worked on the games I slowly started adding more and more and found myself penning a third title much smaller than the other two which would simply service as an introduction to the story and the world. With all of this, I had in the background been working on an engine which I was hoping to power it all.
The Engine's initial iteration was a cobble together mess of different packages from the days of XNA crunched together to build something that might be able to support a game. The largest issue with this first iteration was that if I wanted a feature I simply sought out some piece of code to implement it or library, with no regard to how it fit into the overall project. This led after about a year or so to a redesign attempting to write majority of the base myself, which at the time was a little out of my league but I tried anyways. The impediment to this iteration did not come until a couple years later when XNA final was discontinued by Microsoft. I realized that the way I had engineered the entire project it was dependent on XNA and the two could not be divorced from one another. This led to a new restart which was first built on a wrapper for any underlying graphics technologies in order to allow me to move from one to another simply by rewriting the wrapper. Here the big hurdle was that I moved away from a high abstraction library, XNA, to a low abstraction library, SlimDX. This led to a large amount of time trying to learn and understand directX which was a large boon when new features to DirectX led me to moving to SharpDX as my managed DirectX wrapper. Throughout this time, I was also struggling with the design of the engine and how to handle it, mainly focusing on an inheritance design, but that seemed to bloat quickly. This led to many small attempt to figure out different designs. Then in early 2017 I went to my first Game Developer's Conference. There I listened to several talks about the new game Overwatch and how their engine worked. Here I learned about a design which was similar in principle to component design which I had also attempted but was finding shortcomings in intercomponent communication, and component complexity. Overwatch was built on an ECS design which divorced the data from the systems which worked on it. Here the systems could be made and tested and remade with little effects to the data. So long as the systems consumed the same data and outputted the same data the engine did not care how the system accomplished it. From here I started to learn as much as I could about ECS and general engine design. For which I bought a book called Game Engine Architecture by Jason Gregory, which I highly reccomend to anyone looking to understand game engines. For the next several years I attempted different methods to implement ECS, finally landing on the methods used in this iteration.
As of 2020, I started work on the current iteration with the goal of creating a small metroidvania to work with it. This has helped to keep the work focused on what I need for a specific game rather than blindly building systems. Over roughly the past five years, I've slowly chipped away at building up first the core systems and then focusing on rendering before moving onto input and then more complex systems. The engine has been built in a layered method so that there is a few core rendering systems which handle most of the low-level interactions and the higher you go the less low-leve api interactions you need. Though you can insert systems which interact lower at any point.
One of my favorite moments while working on this project was when I decided to run an optimization pass after the initial triangle test. I wanted to improve occupancy on the GPU and speed up frame times so that meant I needed to make sure I was copying data to the GPU as soon as possible while rendering was still happening. This led me to building a few systems to buffer the next frame's data and copy it to a buffer on the GPU and then the GPU would copy from the buffer to the final locations right before draws. This sort of change even this early could take a while in a built up engine like this since it is built of different interlocking systems, but overall it took about 2 weeks to prototype and build. This shows how well the modular design paid off as I was just able to remove the old rendering systems and replace them with the new ones with minimal touching to other parts of the engine.