DevLog #2 – A journey from 2D to 3D
It has been a while since our first devlog but we have been working hard on one core aspect of the game and it’s now working well enough to start talking about it!
In project “Stasis”, although we use a top-down 2D view, we want the player to be able to move in a complex world with floors of several heights. Moreover, the player should be able to move freely between floors, for instance through using stairs and jump pads, jumping on platforms, or jumping off cliffs. For this, we take inspiration from great games like CrossCode from Radical Fish or Sea of Stars currently developed by Sabotage (respectively made with Impact and Unity). Getting these complex movement mechanics to a robust and satisfying state quickly appeared very challenging. Let’s see how we do this with the Godot engine!
First, we opened our favorite pixel drawing software, aka Aseprite, and made an example of what we expect levels to look like. Here is what we got:
First try and problems
We then tried to implement this in the Godot 2D engine using several overlapping Tilemap nodes and got it quite easily. When we look at a static scene, it renders nicely as expected.
However, things get more complicated if we add a player that can move in the scene and many problems then arise. We can classify them into two mains categories:
- Rendering: the 2D rendering algorithm does not work per pixel but per object, for example sorting nodes on their
yposition values. This works well for many 2D games, but clearly fails to render the depth we want in our project. For instance, the player should appear on top of floor tiles when standing on it, but also sometimes behind it as illustrated below. Sorting
yvalues does not allow to handle these cases.
- Collisions: the player should collide with some walls but also not be affected by them if walking on higher floors. Using one collision layer per floor allows to workaround this problem to some extent, but not entirely. Remember that project “Stasis” will have shooter features. This means we should also set more layers to account for bullet collisions. This quickly gets out of hand and does not work reliably in many cases.
Ideas and dead ends
After some experimenting, it appeared to us that we wouldn’t get things working the way we want them to by sticking to the 2D engine. We thus thought about several solutions:
- Using a hybrid approach: work with 2D nodes, but secretly build a 3D world to compute all physics related stuff and then synchronize the 2D nodes accordingly. This helped a lot with collisions, but did not solve at all the rendering issues.To fix those, we tried to make some custom tweaks to the Godot engine source code. We added a
zfield to Node2D and modified the rendering to sort not by
y - z. This actually worked better than we expected, but it still has limitations with regards to floor tiles. If we had a very blocky world, it might have done the trick, but if you look again at the picture above, you see that we plan to have diagonals to smooth the terrain. This solution wasn’t enough to support that.
- Lower our goals. If we drop the idea of multilevel terrain, things get a lot easier. It it actually still possible to fake several floors visually in 2D and to limit their connections to make it work technically. In some areas of Stardew Valley, you have the feeling that you went up but you actually didn’t. There are stairs to go from one floor to another, but you cannot jump off a cliff, you simply collide against the border. Your visual speed when going up stairs remains the same, and you can clearly see with the slingshot that there is no real depth. These flaws do not matter in this farming sim game, but they would become more annoying for project “Stasis” because of the shooter aspect.
- Lower our goals differently. We can manage multilevel floors in 2D without flaws if they are not directly connected. That is actually what we did in the first prototype of the game and it was working well. Different floors are completely sealed off from one another, and we can only switch through them with teleporters.
- Going full 3D. This one is a bit scary at first. 3D engines are obviously more complex to deal with, and we had more experience working with 2D. This solves all rendering and collisions issues, but introduce two main difficulties: how do we keep a nice pixel-art look? and how do we build content efficiently and not get lost with the tons of features a 3D engine offers that we, for the most part, don’t even need?
The final choice
As you probably guessed with the title of this devlog, we chose the last option: going full 3D. We didn’t want to lower our goals and we felt that this freedom of movement in a multilevel terrain was a fundamental aspect of the project to fully convey the player experience we want to offer. With the hybrid approach failing to solve our problems, we had no choice but to fully embrace the power of 3D.
So here we are today: project “Stasis” is a 3D game in the same way than Enter the Gungeon is. We now have converted most of our previous work to 3D nodes in Godot as you can see below:
This was for sure a lot of work, but now that collisions and rendering are fully functional and robust, we can move forward to work on the next demo we still hope to release before this summer. It might not have as much content as we initially thought, but it seems important to us to have players get their hands on the game at the early stage of development to deliver us some precious feedback.
In the following weeks, we plan to tell you more about these topics:
- How we achieve that 2D pixel-art look while working only with 3D nodes.
- The jumping mechanics to move between floors.
- How we made tools and automatize some tasks to work mainly with the 2D workspace in the Godot editor.
So stay tuned, and leave us a comment if you wish to! We’d happy to read your thoughts and to answer questions!
Until next time!