Dragonbone Engine

The Dragonbone Engine is a C# based game engine, built from scratch by myself. The three pillars of the engine's design are as follows:

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.

Multi Window support is still a feature I do not see in games currently. While it is not universal that every player will have multiple monitors or that every system will support it, it is common for PC users to have mutiple screens. Currently, most games only occupy a single screen and while some technologies allow you to span screens the game is unaware of this taking place and often does not handle it differently. The idea of utilizing multiple windows can allow for games which run on a single machine but give each player their own window or utilize a second monitor to display extra information without cluttering the main play screen.

Design

Modified ECS Diagram
Dragonbone Engine Modified ECS Diagram

The Dragonbone Engine architecture is based on Entity-Component-System(ECS) architecture. In ECS, there is an entity admin which controls the flow of the game. From there the two major elements are components and systems. Components contain game state data, but no logic. Systems contain logic but no state data. Entities exist simply as groupings of components. There is one major rule for systems, which is that they cannot directly call anything from other systems. In order to share data between systems, the systems should use components. In standard ECS implementations, this is simply the game object model.

The version of ECS that the Dragonbone Engine uses is pictured above. The main difference is how the systems are split between render and logic systems. Logic systems are more like your standard ECS system and can read and write to components. Render systems are only allowed to read component data, and not write to them. This allows for the system to have simpler multithreading on render systems. In standard ECS, often the rendering is done outside the ECS for optimization reasons. That means that to bring rendering on par the engine needs to take every advantage it can. The purpose of adding rendering to systems is to unify rendering and game logic into the same architecture. This also has the added benefit of making rendering as replaceable as any game logic system. The rest of the architecture is the same.

Plans and Progress

Currently, the project is divided into 17 stages. Each stage marks an important milestone for the Engine. For example, stage 8: Second Eye, is implementation of 3D rendering and animation. All stages are similarly themed. After each stage the engine will be in a stable state, but will not be feature complete. At the end of each stage is a performance tweak stage, where performance is evaluated and changes will be made to any part of the engine to improve overall performance and stability.

As of right now the engine is currently in stage 2: backbone part 1. In this stage, I am developing a DirectX binding for .Net, DirectX.Net. Once the binding is complete I will be adding classes in the engine to wrap the DirectX calls in a more generic way to allow for a later binding with Vulkan for cross platform support. This stage does not include any performance tweak step due ot the bindings not being implemented into the actual engine and the fact the the bindings themselves will be as lean as possible.

If you are interested in the full plan for the project and the most update status of the project, check it out on Trello.


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 togethered 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.

Even still, in this iteration I have attempted many different implementations of the systems in the engine. My favorite example of this is how the engine schedules systems. The idea of system scheduling came out of talking in a class about processor job scheduling and thinking about how that would be useful in a game engine in order to avoid having to call each system and possibly even systems which may not be necessary being run over a system which should have. From there I initially planned to hand out systems, which would have already planned in advance for which threads each systems would go in and what order systems would be given out in. This would be recalculated each frame to incorporate a priority boost to systems which were not able to run. I built this whole system to do exactly this, then tested it and it took way too long. The scheduling process would have taken up half of the time for a standard game frame, even at low system counts. I then tested what if I just took the prioritized systems and handed them out to threads based on avoiding component conflicts and how long that would take. It dropped frame times immensely. The issue then came from the need to sort this data. I went through several different implementations trying out how to sort the data the best and in the end I found that if I sorted as I inserted each system into the schedule it was relatively fast for the number of systems and seemed to grow linearly with the number of systems. It was actually faster to empty the list and readd all systems one at a time then it was to resort each frame. The issue then lay with the fact that even with all of that optimization that system sorting was still too expensive. This then made me question the key assumption I had made up to this point, do systems need to be rescheduled each frame? Ultimately, I decided that no they didn't, they just needed to be rescheduled every so often factoring in for the aging of the systems. This led to an interesting development as I already had the systems in place to allow a system to run infrequently but consistently if it had high priority. With that the engine could theoretically call a system to reschedule systems on set intervals, but infrequent enough to majorly affect the games framerate.

The most interesting part about this problem was that each step seemed like the unintuitive answer was the right one. Preprocessing seemed like it should be faster, but in the end the computer found it easier to make a quick decision since there were fewer object to worry about(only one per thread). All important systems in games seem to run on the interval of one frame, but that does not alway need to be true even with the most important systems. This led to a design that I think it better than the one I had initially intended, and I keep finding myself doing this with each piece of the engine I build. I often built it test it and if it does not perform as well as I would like I attempt every solution that comes to mind, not matter how ridiculous they seem. Many times the ridiculous options are ridiculous and lead no where, but every once in awhile they pay off.