This project was a great undertaking, where I had the opportunity to work on developing the tooling for a game engine among a team of programmers working on different aspects of the engine. It was also my first time working with dlls.
During the course of this project, I worked on the tooling as stated, alongside the main editor and the Entity Component System (ECS) used.
This was equally my second venture into tool creation since CPlace and CPlace+, moving from a single specialised tool to a full tooling suite for game creation.
This project required a great amount of communication among the team to ensure the tools provided had a good User Experience (UX) and worked alongside the other engine modules such as Graphics and Physics.
The primary editor tooling needed included the basics game world interaction and displays including a Scene Hierarchy, Inspector Window, Debug Console and Menu Bar to allow users to open up closed windows and do other tasks such as saving and loading a scene.
The Scene Hierarchy made use of the Game Objects attached as children to the Scene Root Object, all objects contained a child list, allowing the scene hierarchy to easily display the scene objects and their parent-child relations. Through the Scene Hierarchy, objects can be deleted, duplicated, cut, copied and pasted, alongside the ability to drag and drop objects to reparent them to a new object.
The Inspector window makes use of the currently selected Game Object, displaying its name, transform data and all attached components, under component specific toggleable headers.
Within the inspector, displayed variables can be modified and displayed live, where the user may also add and remove components.
Through the Add Components dropdown, users may also open up the new component window, allowing them to specify a name for a new component.
The Debug console makes use of a global debugger, storing a list of logs up to a specified limit, where the oldest log is removed on exceeding the limit.
The debug calls can be called from anywhere in the program, making use of Log, LogWarning and LogError calls respectively. Within the console, these logs are displayed with the relevant colour and optionally, the file and line the log was called from.
To allow for editing the scene without interference from physics, the scene needed to have pause/play functionality, this simply makes use of a toggle button within the UI, that then enables / disables specific modules within the engines main loop. Equally, on play/pause, the scene is saved and loded respectively to reset the scene to its original state.
The save and load system makes use of tinyXML. The active scene has its objects looped over, recursing into the child objects, storing the name, transform data and child and component count. Equally, for each object, if there are components, these are stored too using the components unique save function.
On saving Components, their class unique type value is stored.
This is used in conjunction with the component factory system, where each component has its own factory class, taking in an object and adding the associated component to it. These factories are stored in a map on the Scene Root (singleton) and indexed via the components class unique type value.
As a result, when loading a scene the loaded in type variable can be used to add the appropriate component to the relevant object.
Following this, the XMLElement can then be passed through to the newly created components Load method to load in the saved member variables for the component.
As stated previously, users can create custom user scripts / components for use in engine. These are created within their own dll that is then loaded in by the engine for use.
This system makes use of the Filewatch library to run a watcher thread that watches the projects scripts folder for changes to user scripts, should a change occur, the active scene is saved and unloaded, the engine is paused and the user scripts dll is rebuilt, once rebuild, the dll is then loaded in and the previous scene is then reloaded in.
As the new dll is a new process, the unloading / reloading of a scene is needed to prevent invalid pointer / memory access.
However, as the new dll is a new process, it has its own unique singleton instaces for the scene root. As a result, any singletons within the engine have needed to be passed through to the user scripts dll on load. This however has improved usability within the user scripts, negating the need for the static access syntax, allosing users to simply use the SceneRoot pointer variable.
The entity component system used is quite simple, where all objects within a scene have a component list, where pointers to components are stored, and each component has a pointer to its parent object.
All components equally have pointers to them stored within their modules to allow for fast looping through all relevant objects on a module update.
Equally, for engine components (Rigidbody etc), the components contain minimal/no functionality, simply acting as containers for variables to be used by the module on update.
However, User Scripts do contain custom functionality alongside Update and FixedUpdate methods as user scripts cannot be batched as such.
The User Scripts are linked to a User Scripts module on addition / deletion, allowing all user scripts to have their update calls ran within one loop.
During this project, I was able to apply my previous learnings from team based projects such as BoxedUp to work more effectively within the team.
This was especially helpful with the Inspector and Component systems, as this required contributions from all members within the team to provide components for their specified module, including the relevant save and load methods and the factories.
I learned a lot when it comes to the structuring of an engine and the separation between the engine and a game produced with the engine, where we made use of a produced game dll that would be consumed by the engine runtime.