Delayed camera rotation in first-person

Godot Version

4.1.3.stable

Question

Hi there! I’m making a first-person horror game and was wondering how to make the turning of the camera delayed. The camera is controlled by mouse movement and I imagine that I’d have to set the new position to a target value, then reach that gradually, but I’m not sure how to do this. Here are my camera related functions in my player script so far.

func _ready():
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _input(event):
	if Input.is_action_pressed("pause"):
		pause_menu.show()
		Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
	
	if event is InputEventMouseMotion and pause_menu.visible == false:
		var MouseEvent = event.relative * mouse_sens
		CameraLook(MouseEvent)

func CameraLook(Movement: Vector2):
	camera_rotation += Movement
	camera_rotation.y = clamp(camera_rotation.y, -1.5, 1.2)
	
	transform.basis = Basis()
	camera_pivot.transform.basis = Basis()
	
	rotate_object_local(Vector3(0, 1, 0), -camera_rotation.x)
	camera_pivot.rotate_object_local(Vector3(1, 0, 0), -camera_rotation.y)

Any help is much appreciated. Thank you!

Have you tried tweening?

Or lerping?

Let me know if you have troubles implementing these in your project.

Hi, thank you for your response. I can’t say I’ve heard of either of these before. How would I go about implementing these to achieve this?

See here my little demo

Here’s the scene structure


Here’s the code attached to the root Node.

extends Node3D

@export var camera_pivot: Node3D
@export var camera: Node3D
var rotation_speed: float = 0.005
var threshold_to_target: float = 0.01
var target_rotation: Vector2
var lerp_speed: float = 0.7

func _ready() -> void:
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		var mouse_motion_event: InputEventMouseMotion = event as InputEventMouseMotion
		target_rotation -= mouse_motion_event.relative * rotation_speed
		target_rotation.y = clampf(target_rotation.y, PI/-2, PI/2)

func _process(delta: float) -> void:
	var horizontal_difference: float = absf(camera_pivot.rotation.y - target_rotation.x)
	if horizontal_difference > threshold_to_target:
		camera_pivot.rotation.y = lerpf(camera_pivot.rotation.y, target_rotation.x, horizontal_difference * lerp_speed)
	
	var vertical_difference: float = absf(camera.rotation.x - target_rotation.y)
	if vertical_difference > threshold_to_target:
		camera.rotation.x = lerpf(camera.rotation.x, target_rotation.y, vertical_difference * lerp_speed)

You need to adjust it to your project, but hopefully this can shed some light how you can use lerp for smoothed transitioning.

2 Likes

This is absolutely fantastic, thank you very much!

1 Like

I have noticed, this is brilliant, but does not work with Godot’s template movement, as the camera does not follow the movement. How can I fix this?

I adjusted the code to be usable with movement as well and simplified it a bit :slight_smile: enjoy


obraz

extends CharacterBody3D

@export var camera: Node3D
var movement_speed: float = 150.0
var rotation_speed: float = 0.005
var ease_curve: float = 0.1
var target_rotation: Vector2

func _ready() -> void:
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		var mouse_motion_event: InputEventMouseMotion = event as InputEventMouseMotion
		target_rotation -= mouse_motion_event.relative * rotation_speed
		target_rotation.y = clampf(target_rotation.y, PI/-2, PI/2)

func _physics_process(delta: float) -> void:
	rotation.y = lerp_angle(rotation.y, target_rotation.x, ease(delta, ease_curve))
	camera.rotation.x = lerp_angle(camera.rotation.x, target_rotation.y, ease(delta, ease_curve))
	
	var movement_direction: Vector3 = Vector3.ZERO
	if Input.is_action_pressed("ui_up"):
		movement_direction += Vector3.FORWARD
	if Input.is_action_pressed("ui_down"):
		movement_direction += Vector3.BACK
	if Input.is_action_pressed("ui_right"):
		movement_direction += Vector3.RIGHT
	if Input.is_action_pressed("ui_left"):
		movement_direction += Vector3.LEFT
	
	velocity = basis * movement_direction * delta * movement_speed
	
	move_and_slide()

Have you tried the InterpolatedCamera plugin? InterpolatedCamera3D - Godot Asset Library It mimics the old behavior and extends it a bit.