Note : Blender 2.83, Godot 3.2.1, Gimp 2.10.18.
Last week, I finally started working on stairs (not something I was particularly looking forward to). Creating stairs and making them work in game is a -big- project ...
The building blocks :
1) 3d modeling (making sure they fit in the alloted space)
2) collision shapes (making sure they're smooth and not too steep)
3) gdscript (walking up and down a slope)
The first thing that needed to be done, was to figure out how I was going to fit in the stairs
between two particular floors. Since there's quite a bit of space to cover (9 m),
and because the stairs cannot be too steep (*), I decided to fit in three relatively short
(*) I figured this out some months ago, in Godot, while experimenting with various kinds of slopes. I finally settled on a max slope of 3 units up over 5 units horizontal.
I also watched a bunch of videos on how to model stairs in Blender. They are mentioned in the list at the bottom of this page.
It's actually not all that difficult to model some basic stairs. It all comes down to the 'array modifier', and an 'empty' when you need to create spiral stairs.
I tried very hard not to have 13 steps in a staircase, but alas ...
I also needed to have some additional walls and ceilings to hide the stairs from view. After a while, my brain started to feel the way this wireframe model looks :
And as if this mess wasn't bad enough, I still needed to add the collision shapes !
When dealing with collision shapes, it's important to remember some simple rules :
1) Keep the shapes as simple as possible.
2) Collision shapes have to be -closed- shapes ; don't remove faces that you feel aren't important !
3) Don't use planes, unless in some special cases. I read that in Godot 'plane' collision shapes are infinite !
Since my character is moving around at a rather sedated pace, a collision shape thickness of 0.1 Blender units works fine for me.
It has been such a long time since I did any coding in Godot, that I can't actually remember the code I used
to have a player character walk up and down slopes. Time for a quick refresher ...
Also, I want to mention explicitly that my brain refuses to understand KinematicBody movement code. So, I'm pretty sure that the code I'm using is ... strange/broken/wrong. But it seems to work, so I'm going with it. 😎
OK. So. Some months ago, I settled on the move_and_slide() method of the KinematicBody node (player). The reason
being that this lets the player 'slide along' obstacles (walls, etc) rather than collide with them and stop (feels
nicer when you play).
The documentation tells me I need to call move_and_slide() from inside the _physics_process() function (something to do with the delta value being automatically taken into account). So, that's what I'm doing (through process_movement()).
The move_and_slide() method has a whole bunch of parameters, but I'm only specifying the first 3 :
1) linear_velocity : the velocity vector (m/s). Important : do -not- multiply by delta (physics engine is handling this) !
2) up_direction : used to distinguish wall from floor and ceiling.
3) stop_on_slope : if the player character is standing still on a slope, and the linear_velocity includes gravity, he will start to slide down the slope unless you set this param to 'true'.
The rest of the params have the Godot default values, including the 'floor_max_angle' which is set to 45 degrees.
The move_and_slide() method returns the linear_velocity vector (adjusted for rotation and/or scale after a slide collision), but I'm not sure how to use this value ... So, I'm ignoring that.
You can also get more information about the collisions with get_slide_collision().
I'm also currently toying with the idea of changing the camera angle when the player character is walking up/down some stairs. The basic setup is like this :
I have an Area node with a BoxShape CollisionShape spanning the entire stairs. When the player enters the Area, the 'is_on_stairs' variable (in the Player code) is set to true.
Note : instancing(*) nodes that contain collsion shapes is a bit tricky in Godot. Changing dimensions of 1 collision shape will also change the dimensions of all other collision shapes in the scene ! Also, since the shapes are shared between various Areas, methods attached to one area will also be triggered by the other areas !
(*) Root node > right-click > 'instance child scene (that contains a collision shape node)', OR, existing node (that contains a collision shape) > duplicate.
The reason for this behavior is that the 'Shape' used to define the CollisionShape dimensions
is a 'Resource' (a data container) and not a node !
In Godot, all resources are shared. Meaning : if that data 'exists' somewhere in a scene, it will be reused whenever you try to add another Shape data container.
So, to solve our problem, we need to set 'unique subresource' on the CollisionShape node.
This whole setup is not terribly convenient : the 'uniqueness' property is hidden in a dropdown menu, and for
some reason it is not a checkbox. Also, it is not accessible from code.
So, there's not really an easy way to make sure that different shapes in the scene are not linked (unless you create the whole setup from scratch every time, which is also inconvenient).
My 'hack' is two-fold :
1) to no longer define a shape, so I'm forced to create one whenever I instantiate a zone for the stairs in my scene.
2) make a special group for these 'zones' so I can check in code if shapes have been shared somewhere (in case I accidentally duplicate a zone in a scene ... which I just know I will do at some point). You can do this by looping over all members of the group, and checking whether 2 or more CollisionShape.shape's are the same (if cs1.shape == cs2.shape). You can trigger this code for instance whenever you run your game from the editor (F5).
Anyway, this is how I'm using the setup in the Player code :
If I decide to keep this feature in the game, I will of course need to refine it a bit. 🙂
This is what it currently looks like in Godot :
Versus 'regular camera' :
I think going down the stairs definitely looks better with the camera angle adjustment (maybe I should point the camera even further down). I'm still unsure about going up the stairs, as well as resetting the camera angle when the player pauses on the stairs ...
Fortunately, I still have plenty of time to decide on this. 🙂
Walking up and down the stairs ...
Looking up ...
Huh ? Where's that hole coming from ???
It's the floor ! It has backface culling enabled ! 😮