Logo 3.5Cats_AndHalfAFish


Follow camera.

April 04, 2019 Bethee, Godot
A simple follow camera setup (Godot).

Disclaimer : I don't claim to know what I'm doing ; I'm discovering things as I go.

I've updated to Godot 3.1.
The purpose is to get a simple follow camera working in Godot (I expect a lot of tweaking in the future =)).

There are 2 types of camera in Godot : the 'regular' Camera, and the 'InterpolatedCamera'.
The Camera can be used both as a static camera and as a follow cam (make the cam a child of the mesh, or use code).
If you want to set up a quick follow cam with smooth movement, you can use the InterpolatedCamera (as I did below).

It is possible to have more than one camera in your scene. Just remember to set the 'current' property of the active camera to true.

If you're used to the Blender camera icon : beware !!! =)
The Blender camera looks in the Y direction ; the Godot camera looks at -Z !

Blender camera.
The Blender camera points to the Y axis.
Godot camera.
The Godot camera points to the -Z axis.

This is a simple scene with a follow cam :

Setting up a simple follow cam in Godot.
Setting up a simple follow cam in Godot (InterpolatedCamera).

Select the InterpolatedCamera node and enable the properties 'current' to set it as the active camera, and 'enabled' to let the camera move automatically (= without code) to its 'target'. Since I want the camera to follow the egg mesh, I've added a Position3D node as a child of Egg and set that as the target for the camera.
Notice that this will not make the camera move in the preview window ! You'll need to run the game (F6) to actually see the camera move to the position of the Position3D node.
If you do not activate the 'enable' property, the camera will not move to the Position3D node location ; it will act just like the regular Camera node.
You can also set the speed with which the camera will move to its position.

This is the result :

Not quite what I expected to see ... The follow cam rotates and ends up looking in the opposite direction !

The explanation is simple.
The Godot documentation says : "An InterpolatedCamera is a Camera which smoothly moves to match a target node's position and rotation." If you take a look at the previous image, you'll notice that the local axes of the camera and the Position3D node do not match. The solution is to rotate the Position3D node around the Y axis until it matches the desired camera rotation (in my case : 180 degrees).

Scene setup in Godot.
Test scene setup in Godot.

To really see the follow cam in action, we need to set up a scene where our model can actually move. Again, there are several ways in which that can be accomplished, but I'm going to use a KinematicBody node.

Setting up a KinematicBody node.
A KinematicBody node (Bethee) with CollisionShape and MeshInstance.

A MeshInstance with a KinematicBody and CollisionShape is a useful combination if you want to create a player character that listens to user input (mouse clicks, key presses, ...) and interacts with the game world (cannot walk through walls, can walk up slopes, ...).
As mentioned in the 2d Godot documentation (but unfortunately not in the 3d documentation), the physics engine can’t handle 'scale' on most types of shapes, so always change the collision shape with the red handles in the viewport !

We also need to change the script from the previous post a bit, to let the player know how fast to move and in what direction.
There are 3 convenience methods that can be used for this : move_and_collide(), move_and_slide(), and move_and_slide_with_snap(). As far as I can tell, these methods can be used interchangeably but have increasing levels of control / complexity. All 3 are meant to be used inside of _physics_process() instead of _process(). Since I'm not really sure how to use the third method (or under which conditions), I'll skip that one.

The simplest method is move_and_collide(). It only requires 1 bit of information : the direction in which you want the character to move. In return, it will give you collision information.
Edit : this was true for Godot 3.0 ; in Godot 3.1 the method has additional parameters but they are all optional.

The move_and_slide() method requires 2 parameters : the velocity (= direction and speed) and the floor normal (= up). There are additional parameters which give you access to eg. 'floor_max_angle' (in radians) and 'stop_on_slopes. With the first param, you can define how steep a slope can get before it turns into a wall (=non-walkable). The default is 0.785398 (45 degrees). With the second param you can make a character stand still on a slope, or slide back down.

I have found it easiest to use both methods at the same time : the collide method for dealing with gravity, and the slide method for moving back and forth.

Example code for the KinematicBody node.
Example code for the KinematicBody node.

(1) The Godot documentation tells us we should 'reset' the axes from time to time, if we use rotations. Precision errors with floats (angles) would otherwise cause the 3 axes to no longer be orthogonal after a while.
(2) I'm using the collide() function for gravity. Notice that the first parameter of this function (the only param that's required) has to be a displacement (unit : meters). Hence the multiplication by delta.
(3) For movement, I'm using the slide() function. The first param here is in 'm/sec' so you should not multiply by delta !

The correct animation is played from the _process() method :

Example code for the KinematicBody node.
Example code for the KinematicBody node.

The result : the character slides back down the slope (very slowly =)) when it stops walking, and it is blocked by a sufficiently high wall. AAAAaaaa ...

Mission accomplished.

Personal note : Even though it takes time to immortalize my game dev meanderings in these blog posts, I think it's worth it (for me).
It can be very frustrating to learn new things and often it feels as if you're not making any progress at all. Having these posts to return to, helps me remember that I -have- done something, and, in the process, 'learned' something. =)