If you have followed our production log videos and our weekly uploads, you have probably seen a lot of our design and asset creation process. As the production of Project DogWalk is now in full gear, I wanted to take a moment to share an overview of our level editing workflow. This post serves as a snapshot of our current process—some details may evolve as we continue refining our workflow!
DogWalk takes place in a beautifully hand-made miniature world. Influenced by paper-craft and stop-motion animation, Julien and Vivien set out to create a charming, tactile environment. We wanted to fully leverage our experience with creating rich and complex worlds-but this time interactive and viewable from many different angles!
While researching level-building in Godot, we concluded that most people use Blender and external editors mainly to author individual assets. The final levels often get assembled directly in the Godot Editor which allows more flexibility when tying them together with the game logic.
Godot relies heavily on Blender's own GLTF export - even when directly referencing .blend files, it calls the export extension behind the scenes. While providing a very smooth and full-featured interoperability workflow, one of its downsides is that it needs to apply the geometry of linked assets instead of referencing it, which creates unnecessary data and makes the updating process more cumbersome. I should note that that's of course by design - it's not the GLTF export's job to provide a complete gamedev pipeline.
Our goal from the get-go was to construct our levels as much as possible inside Blender and keep the linking hierarchy intact. This meant we needed to develop custom tools on top of the GLTF exporter that keep track of the assets, restore the linking and de-duplicate textures.
Simon has dedicated a lot of time to create the pipeline, ensuring it remains as smooth and efficient as possible. Julien Duroure, who is the main developer behind the GLTF exporter has also been super helpful in supporting us, fixing bugs and making our custom pipeline work.
Stay tuned for a more in-depth article by Simon about our Godot pipeline. In the meantime feel free to check out our on-going research here.
Keeping our terminology from the past movie projects, we call the level assets "Sets". As in our movies, they mainly serve as containers for the ground with scattered library assets on top. The main level world.blend consists of (at this time) three areas: The Hub, where Pinda has built the snowman. Clearing, which is a more dense, forest area. And lastly the Fence, which houses a little feeding station for animals.
One challenge was posed by the way we decided to handle collision shapes. Each of our exported assets gets turned into a Node3d in Godot. If the set was a single monolithic asset, this would only allow for one type of collision (Static, or Pass-through) per set. Also, we wouldn't be able to split collision assets into individual layers in order to let Pinda react differently to certain collisions than Chocomel. This way it's also easier to attach individual scripts and game logic to parts of the level.
To allow for all of this, we are came up with what we call Set Layers. These are basically just collections within a set file that function as individual GLTF-exportable assets. By instancing these collections into the main level collection our export pipeline recreates that reference on import.
We use the following prefixes to identify our asset collections:
CH
→ CharacterPR
→ Asset is a (rigged) prop from a single sourceLI
→ Asset is part of a library of instancable set dressing elementsSE
→ Main Set CollectionSL
→ Individual Set LayersA key advantage of this system is that it allows us to reference as many external Blender files from the world map as we want. This means multiple artists can work on different parts of the same level simultaneously.
To maintain a high level of modularity, we also rely on procedural setups using Geometry Nodes. This allows us to generate complex elements dynamically while preserving flexibility in level design.
The creek, the pond and the pathways across the environment had to allow for such continuous updates as the game-play gets refined. Their individual node systems use curves as inputs and generate fully UV-mapped geometry with attributes for shading and texturing.
After some initial concepts by Vivien and some tests in Blender we settled on a look for the creek that resembles stop-motion animated plastic foil. The animation is a simple animated UV offset which we re-created in Godot.
The creek geometry consists of:
The creek gets cut out of the ground mesh to allow for dynamic placement in the world.
The paths also rely on curve inputs, with UVs automatically calculated along their length. We use a texture atlas to define the main path texture, alternate segments, and start/end caps.
To help with orientation and differentiate each region of the world, we use different path textures. In addition to bigger paths we have smaller, more faint desire paths to nudge the player into different directions.
Most of the environment assets such as rocks and trees are carefully placed by hand. For the grass and reeds I started out by scattering them with Geometry Nodes. Initially there was no way to convert them into individual instanced objects, making it difficult to refine placement manually. However, thanks to Jacques and Simon we now have a "Visual Geometry to Objects" operator, we can now apply Geometry Nodes results as editable objects giving us the best of both worlds!
Spread throughout the world are patches of deeper snow. We use them as traversal challenges to slow down the kid's movement. These patches visually resemble torn pieces of paper layered onto the ground.
Originally, we used scanned images of real torn paper mapped onto meshes- each of them with its own unique shape. Although these were visually beautiful, the downside was that adjusting these shapes caused texture distortions, making variations difficult to manage.
To solve this, we made a Geometry Nodes setup that takes a closed curve and generates a dynamic, resolution-adjustable mesh island. The island has a main UV for the surface detail and another UV on a strip along the edge to map a tilable tear texture to control the transparency. This allows us to tweak shapes more freely while keeping a similar aesthetic.
Each snow patch can also have a corresponding collision shape which gets generated automatically.
In general, exporting levels is a smooth and painless process. Each Area or their individual Set layers can be updated individually. And since all assets are references, we don’t need to re-export the entire set each time some geometry changes—a massive time saver!
One thing we’ve noticed, though, is that we often need to delete and regenerate the Godot cache to ensure that updates apply correctly. It’s a small but necessary step to keep everything running smoothly.
This workflow represents the current state of our pipeline, but as we continue refining the game, we may tweak and improve certain aspects of it. If you’re interested in more behind-the-scenes insights, follow our weekly production logs!
Join to leave a comment.