The idea of the contents of the directory (
keh_nodes) is to hold addons that are pre-made nodes with attached scripts. Those can be directly used by simply adding a scene instance wherever the functionality is desired.
For the moment there is a single node:
The main objective of this node is to provide a "camera arm" that can be attached into a different node (the target) and keep the camera at an specified distance. The available features are:
To start using the
Cam3D all that is necessary is add a scene instance into the desired target object. Normally this will be attached into the player character. After that, the properties can be configured within the editor itself.
In this how to use you will find explanation for those properties up to the
Max Shake Offset, which is the last
Cam3D actual property. The rest is actually exposing the original
Camera properties so, if needed, you can change them.
Also, every single GDScript snippet will assume you have an instance of the
cam3d. Maybe retrieved like this:
# some properties.... var cam3d: Cam3D func _ready() -> void: # Some code... cam3d = $player_character/Cam3D
This should be an obvious property and it determines how far (or how close) the camera will be from the target object. This is in the Godot's 3D unit.
If you want to manipulate this value from GDScript, you can directly access the property
# Get the arm length of the Cam3D var length1: float = cam3d.arm_length # Set the arm length of the Cam3D cam3d.arm_length = 10.5
This property is meant to prevent the camera from changing its orientation with the target. So, if you only keep
Roll enabled, the camera will change its pitch and its yaw when the target does so, however the camera roll will not change. Some example use cases:
X. Then all 3 angles get locked. The camera will still follow the target but will never change its orientation with character, only if manually done through script.
To manually change those values through script, you have to keep in mind that the property itself,
lock_rotation, is meant to be a bit mask, controlled by the flags
FLR_YAW. To facilitate here and avoid the necessity of having to deal with bit masking operations, there are several functions meant to deal with this property:
set_lock_pitch(bool): this allow you to enable or disable the pitch locking.
set_lock_yaw(bool): this allow you to enable or disable the yaw locking.
set_locK_roll(bool): this allow you to enable or disable the roll locking.
is_pitch_locked(): returns true if the pitch is locked.
is_yaw_locked(): returns true if the yaw is locked.
is_roll_locked(): returns true if the roll is locked.
The camera can be configured to interpolate its transform when following the target. This is most useful when the target's visual representation is actually smoothed by interpolation. While you can individually enable/disable rotation and translation, ideally both should be at the same state. The reason those are separated goes from the fact that there was one bug on a different addon (
network) but I believed the culprit was caused by the interpolation method. Once the actual bug got fixed the camera's interpolation remained here.
To directly manipulate those settings from GDScript:
# Get the state of pivot interpolation var is_interpolating_pivot: bool = cam3d.interpolate_pivot # Get the state of orientation interpolation var is_interpolating_orient: bool = cam3d.interpolate_orientation # Enable interpolation on both translation and orientation cam3d.interpolate_pivot = true cam3d.interpolate_orientation = true
These two properties are directly connected, so dealing with them in a single topic.
The lag speed will be completely ignored if the lag is set to
None. From the drop down menu you can change the lag mode to
Smooth Stop or
Smooth Start. In either case the camera will have a certain delay behind the target, controlled by the
Lag Speed property. The lower this number, the more it will lag behind a moving target.
From GDScript you can change (or retrieve) those properties:
# Get current camera lag speed var lag_speed: float = cam3d.lag_speed # Set lag speed cam3d.lag_speed = 0.01 # Get current lag mode var lag_mode: int = cam3d.camera_lag # Change to smooth start cam3d.camera_lag = Cam3D.CameraLag.SmoothStart # Or to smooth stop cam3d.camera_lag = Cam3D.CameraLag.SmoothStop # Or disable the camera lag cam3d.camera_lag = Cam3D.CameraLag.None
This property determines how the camera will behave when something is detected between the camera and the target object. Note that currently this detection is done by a single ray cast and depending on the geometry of the objects it may miss something. Nevertheless, the available methods are:
None: nothing will be done and objects will keep blocking the view.
ShrinkArm: the arm length will reduce its size so it's placed right in front of the blocking object. The length will be restored to its desired value when no object is blocking the path.
CullObstructing: from the first detected object until the camera, everything will be culled. This has the effect that some objects (ground for instance) will probably get partially culled.
HideObstructing: every detected object between the camera and the target will be hidden. Be careful with this mode in multiplayer games because hidden objects will not result in collisions for other objects.
Which method you will use largely depends on the project and the desired effect. Nevertheless, to deal with that from GDScript:
# Get current collision mode var coll_mode: int = cam3d.collision_mode # Change collision mode to none cam3d.collision_mode = Cam3D.CollisionMode.None # Or to Shrink Arm cam3d.collision_mode = Cam3D.CollisionMode.ShrinkArm # Or to cull obstructing cam3d.collision_mode = Cam3D.CollisionMode.CullObstructing # Or to hide obstructing cam3d.collision_mode = Cam3D.CollisionMode.HideObstructing
This property works exactly like the mask in the various collision shape objects. In other words, the enabled layers will be used to match objects. So, if you want to have some objects to be completely ignored by the collision checking, make sure those objects are part of a layer that is not enabled in this property.
In order to avoid having to deal with bit mask operations from code, two functions have been provided to deal with this aspect of the property,
set_collision_layer() allows one to enable or disable an specified collision layer index, matching the labels shown in the inspector. In other words, the index in here begins from 1 rather than 0.
is_collision_layer_enabled() will return true if the specified layer index (again, starting from 1 rather than 0) is enabled.
The following snippet performs some silly manipulations:
# Disable collision Layer 2 cam3d.set_collision_layer(false, 2) # Check if collision layer 5 is enabled and, if not, enabled it if (!cam3d.is_collision_layer_enabled(5)): cam3d.set_collision_layer(true, 5)
This is another bit mask property, that can be used to individually enable/disable the possible traumas that can be applied to the camera when shaking it. Specifically, the shake can change the rotation, the orientation or both. As with the rotation lock, there are some functions to avoid having to directly deal with the masks:
set_shake_rotate(bool): enable or disable the rotation of the camera when shaking
set_shake_translate(bool): enable or disable the translation of the camera when shaking
is_shake_rotate_enabled(): returns true if the rotation of the camera when shaking is enabled
is_shake_translate_enabled(): returns true if the translation of the camera when shaking is enabled
These two properties determine how the shake will behave. The trauma decay indicate how fast it will stop shaking while the frequency how fast it will shake. Ok, things will probably get clearer with the explanation of how to use the shake system.
To make the camera shake you basically call the function
add_trauma(amount). By calling this with a positive number the camera will then have some amount of trauma which will then cause it to shake during the update. At that point,
trauma_decay will be removed from the camera's trauma and when it reaches 0, it stops shaking. But the
shake_frequency determines how fast the camera will actually shake. So, one thing to keep in mind here is that the trauma is always kept in range [0 .. 1].
From GDScript, this snippet shows how to change those properties and then cause the camera to shake:
# Get current trauma decay var current_decay: float = cam3d.trauma_decay # Get current shake frequency var current_frequency: float = cam3d.shake_frequency # Change decay rate cam3d.trauma_decay = 0.5 # Change frequency cam3d.shake_frequency = 3.5 # Add maximum trauma cam3d.add_trauma(1.0)
Each of these two properties is a
Vector3, giving more control over the shaking behavior. Each one specifies the maximum amount that each component can be changed while shaking. So, for example, if the
MaxShakeOffset.x is set to
2, then the final X coordinate of the camera will be changed by something in between -2 and +2. The same applies to any other component.
Manipulating those from GDScript:
# Get current maximum shake rotation var shake_rot: Vector3 = cam3d.max_shake_rotation # Get current maximum shake translation var shake_translation: Vector3 = cam3d.max_shake_offset # Change the maximum rotation cam3d.max_shake_rotation = Vector3(1.0, 1.5, 1.2) # Change the maximum offset cam3d.max_shake_offset = Vector3(2.0, 2.2, 2.5)
Obviously you can access each vector's component:
# Change just the maximum offset X when shaking cam3d.max_shake_offset.x = 2.1