I’m sure, some will smile on me trying to even make such comparison. But I talk from the perspective of a beginner/intermediate in both engines, although I’ve spent at least 2x the time in UE4. I also have decades of experience with custom engines.
- Creating custom vertex format for the terrain. I don’t need tangent space for low poly, and can chose between vertex colors and texture coordinates (but don’t need both). I want to be able to render huge scale terrain, so optimizing for what I really need, is good idea.
In Godot, the process is very streamlined. You pass flags to what kind of vertex components you need. And the vertex format is created for you, you don’t need to deal with anything. You don’t have super low level control (like data format), but it does the job.
In UE4, you must implement FVertexFactory. And that’s not a joke. It goes together with ush file that must implement the shader code. You can use the one from FLocalVertexFactory as a template, but keep in mind, it’s 50K since it’s reused by most vertex factories in UE, and a lot of things are going on there. Also, be prepared to compile thousands of shader permutations for this ush, although you will be using it just for this one place. In theory, there is ShouldCompilePermutation() method, but I still had to compile a thousand…
I wasted most of my time trying to understand the PrimitiveID stream though, used for auto instancing. While I don’t need it, I got asserts and crashes when I tried to disable it. Obviously I still don’t fully get how it works. But my point it, the process is magnitudes more complicated then in Godot. Be prepared for serious work if you deviate from the standard UE4 vertex format.
- Actually drawing. This process is of course creating the GPU buffers and issuing draw commands
In Godot, this is again very streamlined. You fill up a standard looking mesh structure and you are done. My complain however, is that you can’t directly specify the GPU buffers, which means you can’t share them. In my terrain case, I can share the index buffer, since the terrain tiles will share the same topology.
In UE4, you use FMeshBatch, which is a bit strange. First, if you forget to set LODIndex to 0, your mesh will not render. I had this issue, and had to read the source code of VoxelPlugin to see a comment there. By default it’s set to INDEX_NONE, and that will not work. Second, I still have no idea where the bounding box for the mesh batch comes from. As I mentioned, my terrain will generate multiple tiles, and each tile will draw as a separate batch. So proper culling is essential. However , it looks the batch has no box, so it probably gets it from the FPrimitiveSceneProxy (which is quite bad). Maybe it calculates it automatically? Could not find it in the source code. FPrimitiveSceneProxy has calls for both dynamic and static rendering paths, which also makes the code more complicated. I would prefer single, but more cleverly designed interface, since at the end, you setup very similar things.
- Assigning material to the primitive
In UE4, UPrimitiveComponent has material interface, which is displayed in the property list of the editor. However when you try to change the material, it doesn’t work! I debugged the code, and what I found - the editor is hard-coded to handle static primitives, landscape, other UE types, but for custom primitives - it just skips setting the material… So I had to remove that interface, and implement my own material property, in order to allow changing the material. Very bad programming there. Just one more virtual method was needed to make the code general and handle custom primitives…
- Writing custom material
In Godot, you can directly write custom shader code. For low poly look, the normal can be constructed by the derivatives of the position. It’s literally one liner. I strongly prefer this approach instead of nodes. Otherwise you depend on what is exposed.
Back to UE4, I found the nodes, plugged them in the normal input (as the tutorials say) and… it doesn’t work. The normal must be in tangent space… But I don’t have tangent space. There isn’t even a node to convert from world to tangent space (which is why I hate nodes). After many hours of searching, I finally found it - a checkbox in the material that toggles the normal input from tangent to world space. This is exactly the kind of stuff I hate in these high level node systems. You depend on the presence of nodes and check boxes, and if one thing is missing, you will be not able to do what you want. IMO, it’s terrible idea of UE not supporting custom shaders. AFAIK, you can have custom shaders for post processing, but not for materials (like Godot). Yes, I know the material system in Godot is much more basic, but as they say - when there is will, there is way. In UE4, this just wasn’t part of the design goals.
- Generate random terrain features.
For this, you need noise. Godot comes with OpenSimplexNoise out of the box. I failed to find something similar in UE4, so I had to add it manually.
- Generate random samples on the terrain (for example - erosion).
In Godot, you have PCG random generator, which is modern and often used. In UE4, you have FRandomStream, which is using absolutely primitive LCG: 0x3F800000U | (Seed >> 9). Are you kidding me?
- Multithreading. My terrain generation code should run in parallel of course
In Godot, you generally reuse STL, but they have some utility functions like Semaphores and a way to spawn batches of work. I would say, quite primitive, but it’s all based on STL and I’m fine with that.
In UE4, you have basic task system and FRunable… Again, quite primitive. For example - there isn’t even a Semaphore. I found local implementation in the Lightmass and Live Codding. The kind of stuff you can expect when the engine itself is lacking functionality, and the writers of a component had to hack something in their own code.
- Exposing terrain functionality to scripting. This is a side topic, but nevertheless part of developing custom components.
In Godot, there is some boiler plate code to write. But it’s not big deal. I like GDScript and the scripting system overall.
UE4 requires less code, but you must deal with the custom build system which is slower for iterative changes. Also, I’m not a fan of BPs. Didn’t even duplicated the functionality I had in Godot (to randomly place trees, and basic terrain modifications). I just lost will.
Overall, after this experience, I think I underestimated Godot, and overestimated UE. Godot fully does the job it was designed for. You can find almost all components you will need, you can work quickly and design custom things… within reason. Knowing C++ can give you more power and flexibility. Its only problem is that the 4.0 branch is very unstable and the progress is very slow…
I expected my implementation in UE4 to take longer. But it was much slower to develop (3-4x compared to Godot). My observation is that UE4 is missing what I most need from an engine - the middle level to help you build things quicker. It has very low level layer you can fiddle with, and very high level - but nothing in between. Kind of what you get from BP vs C++. You must be either scripter/artist or advanced engine programmer. And even being an engine programmer, you must go with the general design of the engine. It’s not a flexible framework, that can easily adapt to your needs. Writing C++ code is painful compared to writing C++ for Godot. You get lost in complex and not documented code. And while the code base is huge, you still find missing pieces that slow your productivity. So, in the context of Indie gamedev, yes Unreal is practically free (you are not going to make much money anyway), but not always the right tool. C++ is generally a powerful tool to interact with the engine and gain more control. In UE4 however, it’s not really a “productivity tool”, you must actually write advanced engine code. Which also raises the question - what happens with this complex custom code in the next UE version, when internals change? It’s not worth it, if you are not working on a AAA game, like Epic does. For the rest of us, what you see is what you get. Which of course is a lot, don’t get me wrong. But at least for me, as a programmer that want to be productive on this middle level/ power user, maybe UE is not the best option out there.