L;DR: I used 2D pixelart assets, UV project from view in Blender to project the pixelart onto geometry, GridMap and Sprite3D in billboard mode.
Project Source code and blend asset downloads (all MIT/CC0
For a long time I thought making 2D games and 3D games are two vastly different things, especially if you are working in a dedicated 2D/3D engine. While Godot does come with a separate 2D engine, the 2D to 3D workflow is actually much more seamless than one might think. How much 3D or 2D someone wants in their game, is thanks to Godot flexible toolset, completely up to the user. It's not a binary question, it's a smooth gradient and more a matter of choice. Besides not forcing a specific workflow onto the game dev, neither does Godot force the game dev to a specific art style.
With this little side project I was trying to figure out how close I can come to a pixel perfect 2D looking isometric top-down platformer, but utilise all the benefits of the 3D engine to help with platforming and sorting issues. My goal was to come up with a simple workflow that would allow me to design quickly and as much as possible in 2D (gameplay, assets, levels), yet avoid the hassle of writing a custom third axis for the 2D engine: The best of both worlds™.
Complexity here is the key differentiator. 2D will always be faster and easier to work with than 3D. But when your custom third axis needs almost as many features as the build in third axis of a 3D engine, the difficulty and complexity curve flips and it's probably time to consider whether switching to the 3D engine is a viable option. GDQuest also made a very good video about this. When exactly this point reached, is very different form game dev to game dev. For me this point is reached with multiple platform heights, stackable objects the player can jump on, entities that need to interact and target each other over multiple platform heights, with believable physics, player control during jumps, ballistic projectiles, cover. All this would be a pain to deal with in with a custom 3rd axis in 2D. Every new gameplay mechanic will increase complexity, while in 3D it's either already build in or pretty much standard procedure.
Mat (retromatn) posted a series of tweets a while ago explaining how he solves some of these problems purely in Godot's 2D engine. Here I'm using the 3D engine which solves a lot of these programming issues automatically, but brings up a few asset creation questions.
My approach is to design characters, animation, tiles and assets as 2D pixelart, and then use blenders "UV project from view" to map those 2D textures onto 3D geometry from the same isometric and orthographic camera view angle I would use in game. The 3D geometry then is imported to Godot, converted into a MeshLibrary which is then used as tiles for the Girdmap.
As a first test, I tried this process with a simple cube. This tutorial by Emil on Game Fiddler explains step by step how to set up a camera in Blender that will render a isometric pixel cube of 64 by 64 pixels. The rendered image can be as basis to for a 2D overpaint in your favourite pixel art graphics program. Back in Blender in the shading panel I removed the bsdf and added Texture>ImageTexture and open the altered 2D texture. Connect "Color" output to the "Surface" input of the Material Output to make it show up in the viewport.
For the UV projection, go to Layout panel select the cube, bring up the UV panel select Image>open in the UV panel and select your altered 2D texture. Then click the camera symbol to get the camera view, hit tab to get into Edit mode, Select all the faces in your isometric camera view and hit "u" to unwrap, select "Project from view".
Now you should now see the UVs lining up with your image texture. If you have "Viewport shading" selected in the Layout panel, you will also see the texture applied in the Layout panel. The last step before exporting is to apply all Transforms (Object > Apply > All Transforms).
I exported as gltf2 including the camera which was set up in Emils tutorial in the beginning. My Blender version does not apply the constraint modifier of the camera, even if you check "apply modifier" on export, but a simple "look_at(Vector3.ZERO,Vector3.UP)" in Godot does the trick. Be sure to check and enable "Include>Data>Camera" in the Blender gltf2 export settings.
In Godot you have to create a new Spatial material for the imported mesh and drop your 2D pixelart overpaint onto the Albedo Texture slot. If set your stretch mode to "viewport" you have no a pixel perfect 3D scene that looks exactly like your 2D texture. If you want a fun comparison, you can add a Sprite3D set enable the billboard flag and set it's pixel size to "0.022" and the texture to the same 2D pixelart texture. The result is two cubes that look exactly the same. I have to admit I found this quite fascinating the first time I saw it:
This is what it looks like in the editor:
https://i.imgur.com/DLfhKb8.mp4.If anyone wants to have a look at the project files thus far, you can check the out here.
One tile down, plenty more tiles to go. The other tiles are simple variations of the basic block I already have, so the are quick to model. When it comes to positioning the tiles and creating your spritesheet for textuing, you have a couple options: create all tiles for in the world centre of the blend scene and make a separate textures for each tile, or one texture and move the UVs around, or, and this is what I did: Set up the geometry in the camera viewport like a spritesheet, render and alter the spritesheet to your hearts content, and then repeat the UV-project-from-view procedure above. This has the advantage that UVs are automatically accurate and exactly where they should be, no human error. It has the downside of having to reposition the geometry into the world center before export though.
This is what the final overpainted pixelart texture for all the tiles looks like:
And here is what the blend scene looks like:
As you can see the Tiles neatly fit the Blender grid. This makes it easy to position them with Transformation orientation: view.
To get the ready for export I positioned the tiles back into the world centre and made sure all Transforms are applied. Since this is my first time making a GridMap, I followed this tutorial by Martin Senges who uses objs as export Filetype for the MeshLibrary.
Feel free to download the blend files from here.
I duplicated the Single-Block Godot project (so I could reuse the camera and project setup) and followed the Gridmap tutorial, which is pretty straight forward and easy. Only in my case I could always use the same 2D pixelart texture for all my tiles with out any additional effort, because of the previously discussed blend scene layout and one click UV project-from-view.
With the Camera and GidMap set up and done, I just needed a player to test the scene. I used the a spritesheet from GrafxKid on opengameart.org as a base (had to change the perspective and add a back view), and a platformer script from Miziziziz as basis for a quick and dirty the character controller.
The cardboard box is made in the same process as the first cube tile, although I experimented with more subdivisions on the box since the box texture likes to glitch out and more subdivisions are suppose to fix that. I think the glitches improved slightly after I have done that, but not completely. I also tried to use a Billboard Sprite3D instead of a projected cube Mesh as box, which results in no glitches at all, but unfortunately in clipping and sorting issues. I suspect the glitchy culprit has to do with micro movement of the RigidBody though ... either that or my lacking 3D asset creation skills.
The Player blob shadow is a Spotlight, set to "negative", the dust (walk, jump) are two separate Particle effects. A WorldEnvironment with "ClearColor" makes sure everything is evenly lit for a 2D look.
One last nice thing I would like to mention is the switch, which is just a Sprite3D in Billboard mode and 2 frames. A static body makes sure there is no clipping and sorting issue here. An Area2D detects the player performs the frame change and blue cylinder spawning.
If you are curious you can check out the final Godot project files in their entirety here.
Thanks to u/sc0tr for inspiring this post!