Every few years I start thinking that maybe it's time to release the struggle of maintaining my own game framework (Plasmacore) and just embrace a mainstream game engine.
Those thoughts came up again as I've been learning Vulkan and implementing a Vulkan renderer for my next-gen remake of Plasmacore. It's been a slog: Vulkan is much more granular and tedious than OpenGL - Vulkan is a lot hairier, you might say.
Don't bother reading all the following, but programming Vulkan is like: create an application info object, create a VkInstance configuration object, query number of available instance extensions, fill buffer with info on all instance extensions, iterate over buffered instance extensions to see what's available, enable desired instance extensions by packing their names in a separate buffer, create the VkInstance, select gpu (query available gpu count + fill buffer with info + iterate over), select a gpu, create a display surface, select a graphics queue family and present queue family to use (query count + fill buffer + iterate), select a surface format (query count + fill buffer + iterate), create a device configuration object, enable desired device extensions (query count + fill buffer + iterate), create the device, create a swapchain configuration object, create the swapchain, fetch the swapchain images, create an image view and a command buffer for each swapchain image... and so on.
Let me add that to load a texture you have to: create a temporary buffer (create a configuration object, create the buffer, query the buffer memory type requirements, create a buffer memory allocation configuration object, allocate the buffer memory, bind the buffer memory to the buffer), copy the image data to the buffer, create an image configuration object, create an image object, create an image view configuration object, create an image view object, get the memory type requirements for the image, create a memory allocation configuration object, allocate the memory, bind the memory to the image, set the image layout (configuration) to be ready for transfer for the temporary buffer, issue a command to transfer the texture data, and then set the image layout to be ready for rendering.
After a couple of weeks of that, with no triangle-drawing even happening yet, taking a break in order to take a closer look at the shiny new Godot 4 started sounding real good.
This was my first time using Godot and let me say I am super impressed! It's a professional-quality IDE that's self-hosted (meaning the Godot IDE is made with Godot). It's fast, responsive, slick, polished, streamlined, and full-featured, from the innovative node-based scene graph to the custom Python-esque scripting language with built-in editor (featuring syntax highlighting and intellisense syntax pop-ups).
In fact, Godot feels like the most professional IDE when compared to the top two commercial game engines, Unity and Unreal. Those two feel slow and bloated in comparison, with Unity having a particularly lackluster UI (that was my memory and then I reinstalled Unity while writing this just to take another look, and... bleh).
I want to point out one UX detail that's quite significant to me: the art style of the Godot IDE is perfect in my opinion. It uses a two-color solid-filled icon style similar to the Metro style that Microsoft popularized in Windows Phone 7 & 8 and Windows 8. Sadly Microsoft then abandoned its perfect style in order to jump on the "thin chicken scratch icons" bandwagon popularized by Apple, but I'm glad the style is alive and well in Godot.
Although I've watched a number of "how to make a game in Godot" videos over the last few months - my interest in Godot continuing to be piqued - I haven't actually made anything beyond "hello world" with Godot yet. That's because I want to postpone learning how to do what's definitely possible and skip ahead to the limitations, to finding out what's not possible. Are there any deal-breakers that will prevent me from making the kinds of games I want to make?
The answer is yes and no. And yes. And no.
I've changed my mind on this point multiple times while writing this blog. As in: I play around with Godot, come to a conclusion, write a little bit more, think of something to try that may upend that conclusion, and repeat.
I believe my final answer is: no, there are no dealbreakers. Godot provides a good - and possibly great - game engine on top of a great technical framework. All the hard stuff (Vulkan) is done and there are thousands of development hours put into a very mature code base by thousands of programmers - the Git log of the main repo contains commits by nearly 2,400 different developers.
By implication: yes, I am planning to stop developing my Plasmacore framework and go Godot! It will be great to reallocate most of the time it takes to maintaining a cross-platform game framework into making games instead. However, I still plan to roll my own development libraries, Rogue-based native extensions, and Rogo-based buildscripts to supplement the scons system that Godot uses.
I'll expand on the seeming dealbreakers with Godot and the ways I'm working around them.
Most potential dealbreakers stem from the fact that game engines are geared towards certain types of games and rendering techniques, which is great if you are in the 98% of game developers who are needing those exact things, but for my needs it's easy to end up going against the grain of an engine and fighting it instead of flowing with it.
For example, ever since 2015 Plasmacore has been a "2DX" framework - 2D that can extend into 3D, or in other words pixel-perfect 2D image-drawing that also supports 3D transformations and full 3D objects as needed.
I have a perfect use case for 2DX: card games. I developed 2DX tech so that I could have nice-looking 2D card games with cards that flipped over in 3D.
Godot, however, strictly segregates 2D and 3D scenes, and places limitations on both that prohibited my 2DX approach - at least out of the box.
Godot's 2D objects can't be transformed in 3D space at all, so 2DX can't be implemented in Godot's 2D mode scenes.
You might think 3D mode would work, because 3D can limit its rendering to two dimensions as well as rotate into 3D. But there's a catch: Godot doesn't support arbitrary 3D transforms, which is what I'd need to be able to easily implement my 2DX system. It only supports limited versions of the most common transforms: perspective, orthogonal, and frustum.
In addition, I much prefer the approach of drawing images procedurally rather than having a scene tree (creating entities or nodes, managing their positions, and letting the engine automatically draw them). It's easy to implement the latter when you have the former, but you can't do the reverse.
I was heartened to see that Godot supports procedural drawing in 2D, but its procedural 3D drawing may not be as robust. It certainly doesn't have the same extensive API for procedural 3D drawing, and the API it does have (the ImmediateMesh class is the best bet from what I can tell) warns of performance penalties if it's leaned on too heavily.
Once again I have a perfect use case for procedural 3D drawing: using the Spine animation system in Godot 3D and drawing arbitrary textured triangles as part of the rendering. Spine has an official Godot plug-in, but it only works in 2D mode. Assuming I've figured out a way to get 2DX working in 3D mode, I've got a whole 'nother hurtle to get Spine working in 3D mode.
What saves these things from being dealbreakers is the open-source nature of Godot and how well-organized and extensible the engine is. If there's something I want Godot to do, that it doesn't do, I can just add it myself.
You can integrate additional C/C++ code and libraries with Godot in three different ways: by implementing extensions, by implementing modules, and by modifying the engine source code and recompiling it.
The engine is amazingly easy to compile. Just clone the repo, install the scons build system, and type
scons in the repo folder. Boom, full rebuild! Well, okay, on macOS you also have to fetch the MoltenVK repo - Vulkan for Apple OS's - and pass the location as a parameter, but NBD.
I've already started my own fork of the Godot engine and have added a 2DX camera mode and a
MeshInstance3D property for overriding the normal depth sorting of nodes (entities) and facilitating the effect of procedural drawing in a scene-tree-based system. If these engine modifications end up gaining any traction with the Godot community then I'll submit a Pull Request, but in the meantime I'm happy to just be able to customize Godot to my own purposes.
I've also started work on a
GDExtend Rogo buildscript to facilitate building
GDExtension libraries. I'll then use
GDExtend to create a Rogue-based native extension template with a message-passing API that can be used to easily implement computationally intensive logic such as a guaranteed-solvable solitaire layout generator.
I'll then make some sort of Plasmacore library for Godot, AKA game development utility library, using some combination of native Rogue and Godot's Python-esque GDScript.
For the sake of completeness I'll mention the reasons I've never switched to Unity or Unreal as I've checked them out from time to time over the years:
- Drab UI that feels unintuitive
- Feels heavyweight and bloated
- No source code access (unless Enterprise or whatever)
- No built-in script editor
- Takes (or used to take) forever to make an Android build of a game (in truth I haven't tried this on Godot yet)
- High but not unreasonable flat-rate license fee for pro developers; still, Godot is free
- Feels even more heavyweight and bloated than Unity
- If you're not making a AAA 3D game, it's too much overhead
- No-code scripting is neat, but would get pretty tedious for any complex scripts
- Significant but not unreasonable royalty-based licensing for pro developers; still, Godot is free
So Godot it is. I'm planning to make all my future games in Godot. I'm very excited about its potential!