Blob lobber was the second project we made in our own engine Wondrous Machine.
This time, we decided to move away from unity as our level editor and fully integrate all needed functionality into the engine. It was mainly me who worked on this huge task and it ended up being the one thing I spent almost the entire project working on.
Throughout the project I looked at other engines like Unity and Unreal for inspiration as to how certain features could be implemented and I had close communication with members of the other disciplines to prioritise which features needed to be added first.
Since I began work on the level editor at the same time as the game project itself, we still had to use Unity to start of and the plan was to switch over to Wondrous Machine somewhere in the middle of the project. However, it turned out to be a larger task than I initially anticipated and it only got to the point where it was fully usable close to the end of the project.
Showcase of most of the basic features in the level editor
One of the major areas I wanted to create was a nice and convenient way to mimic the Inspector in Unity which shows all the components of a selected GameObject and lets you change the various values.
There are multiple ways to approach this and I initially thought of creating an inheritance-based system with custom functions that had to be implemented for components to be editable but I wanted to avoid that to force myself to think outside of my comfort zone a bit but also because I wanted the user to have to write as little new code as possible when implementing new components.
Instead I looked around a bit on the internet and found a tiny library called MetaStuff that through some template magic allows you to register meta data about the members in a class or struct.
It then provides a function that loops over all of the members of an object and calls a user-created lambda for each one while retaining the type of the member!
Utilizing this quality I created a simple templated function for editing a value with a couple of specialized versions of the function for all the different types that I wanted to be able to edit, the compiler will then select the correct function for each member.
The library also made it really simple to do the same thing for serializing/deserializing to and from json or any other file format we might want to add in the future.
The system also allowed the use of more than basic types, such as vectors, custom structs, or vectors of vectors of custom structs! (Figuring out the visual aspect of nestled vectors sure was interesting)
The registration of the SpotlightComponent and the result in the inspector. Except for a few edge cases, this is all you have to do for a component to work in the inspector and for scene serialization
One of the new requirements that came with a level editor was a better way to handle resources than we had previously. Therefore, I implemented UUIDs (Universally Unique Identifiers) to keep track of all the models, textures, materials, and other resources. This made it way easier to serialize objects in the scene and to handle them in the inspector. All of the entities also had a IDComponent that held a UUID, this was used to ensure that the entities always had the same identifiers when loading a scene, the ids that entt (the entity component system we use) generates are not guaranteed to be the same. This was important so that references to other objects were not lost.
I implemented a quite encompassing system for undoing most of the things you can do in a scene using the command design pattern. The system included commands for things like creating/destroying objects, moving them and adding/removing components. But the most interesting case was for changing the value of a member in the inspector, to make that work I had to utilize the previously mentioned member registration system to access the correct member based on its name and using std::any to keep track of what value the member had before and after the command.
One specific tool I created was a tool for editing bezier curves. I built the tool very late in the project and we only used it for one purpose which was to create custom camera movements for some scripted sequences in the game. The system links together multiple cubic bezier curves and presents them to the user in the shape of segments containing a main point that the curve goes through and two control points that are mirrored across the main point.
Sped up footage of the tool in action