- Edited
Hi all, I'm really scratching my head here.
I have my camera setup so that is a third-person controlled camera, with response to collisions. It is set up via a gimbal setup, with a SpringArm3D node and a Camera3D node.
My player is a CharacterBody3D. The camera setup is a child of the player, but is set as top level so it doesn't inherit position/rotation information.
The problem that I'm having is that the camera movement as it follows the player is very smooth, so long as I'm not colliding with something. To illustrate the issue, I attached a bright green sphere as a child of the camera, so that you can see how it behaves here:
I am using Jolt 3D, I am using physics interpolation, and all nodes for the camera setup are set up to not use interpolation.
Any ideas what could be going on here? Any guidance is greatly appreciated! This is my camera control code:
# Variables to expose here
## Mouse Sensitivity
@export var mouse_sensitivity : float = 100.0
## Minimum viewing angle (in degrees)
@export_range(-90.0, 0.0, 0.1, "radians_as_degrees") var min_vertical_angle: float = -PI/2
## Maximum viewing angle (in degrees)
@export_range(0.8, 90.0, 0.1, "radians_as_degrees") var max_vertical_angle: float = PI/4
## The target the camera is to follow
@export var follow_target : NodePath
## The parent that should not be collided against
@export var excluded_parent : NodePath
# Onready vars here
@onready var spring_arm = $"Pivot/SpringArm3D"
@onready var camera = $"Pivot/Camera3D"
@onready var marker = $"Pivot/SpringArm3D/CamHelper"
@onready var pivot = $"Pivot"
# Other variables here
var can_clip : bool = false
var target
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if follow_target and excluded_parent:
target = get_node(follow_target)
spring_arm.add_excluded_object(get_node(excluded_parent))
set_as_top_level(true)
set_physics_interpolation_mode(Node.PHYSICS_INTERPOLATION_MODE_OFF)
func _unhandled_input(event: InputEvent) -> void:
# Handle rotation of the camera here
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
mouse_rotate_camera(event)
# Release/capture the mouse
if event.is_action_pressed("ui_cancel"):
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
@warning_ignore("unused_parameter")
func _process(delta: float) -> void:
_follow_target()
_check_collision()
_rotate_camera()
_clamp_rotation()
func _follow_target():
var tr : Transform3D = target.get_global_transform_interpolated()
global_position = lerp(global_position, tr.origin, 0.05)
func _check_collision() -> void:
# A hit, a hit, a palpable hit!
if spring_arm.get_hit_length() < spring_arm.spring_length:
can_clip = true
else:
can_clip = false
# How to respond in a collision
if can_clip:
if camera.transform.origin.z > marker.transform.origin.z:
camera.transform.origin.z = marker.transform.origin.z
elif camera.transform.origin.z < marker.transform.origin.z:
camera.transform.origin.z = lerp(camera.transform.origin.z, marker.transform.origin.z, 0.05)
else:
camera.transform.origin.z = lerp(camera.transform.origin.z, marker.transform.origin.z, 0.02)
func _rotate_camera():
if Input.mouse_mode == Input.MOUSE_MODE_VISIBLE:
var input_vector = Input.get_vector("look_right", "look_left", "look_down", "look_up")
rotate_y(input_vector.x * 0.05)
pivot.rotate_x(input_vector.y * 0.05)
func mouse_rotate_camera(event: InputEventMouseMotion) -> void:
var viewport_transform: Transform2D = get_tree().root.get_final_transform()
var motion: Vector2 = event.xformed_by(viewport_transform).relative
var degrees_per_unit: float = 0.001
motion *= mouse_sensitivity
motion *= degrees_per_unit
# Don't apply rotation if we're not moving the mouse
if is_zero_approx(motion.length() > 0.01):
return
# Apply pitch
pivot.rotate_object_local(Vector3.LEFT, deg_to_rad(motion.y))
pivot.orthonormalize()
# Apply yaw
rotate_object_local(Vector3.DOWN, deg_to_rad(motion.x))
orthonormalize()
func _clamp_rotation() -> void:
pivot.rotation.x = clamp(pivot.rotation.x, min_vertical_angle, max_vertical_angle)
rotation.y = wrapf(rotation.y, 0.0, TAU)```