Files
godot-demo-holes/demo/assets/scripts/player/camera_effects.gd
Collin Campbell 9f5183bed2 Level Setup
Integrated with Trenchbroom
2025-11-17 19:30:59 -05:00

157 lines
4.8 KiB
GDScript

class_name CameraEffects extends Camera3D
@export_category("References")
@export var player: PlayerController
@export_category("Effects")
@export var enable_tilt: bool = true
@export var enable_fall_kick: bool = true
@export var enable_damage_kick: bool = true
@export var enable_weapon_kick: bool = true
@export var enable_screen_shake: bool = true
@export var enable_head_bob: bool = true
@export_category("Kick & Recoil")
@export_group("Run Tilt")
@export var run_pitch: float = 0.1 # Degrees
@export var run_roll: float = 0.25 # Degrees
@export var max_pitch: float = 1.0 # Degrees
@export var max_roll: float = 2.5 # Degrees
@export_group("Camera Kick")
@export_subgroup("Fall Kick")
@export var fall_time: float = 0.3
@export_subgroup("Damage Kick")
@export var damage_time: float = 0.3
@export_subgroup("Weapon Kick")
@export var weapon_decay: float = 0.5
@export_subgroup("HeadBob")
@export_range(0.0, 0.1, 0.001) var bob_pitch: float = 0.05
@export_range(0.0, 0.1, 0.001) var bob_roll: float = 0.025
@export_range(0.0, 0.04, 0.001) var bob_up: float = 0.005
@export_range(3.0, 8.0, 0.1) var bob_frequency: float = 6.0
# fall kick private vars
var _fall_value: float = 0.0
var _fall_timer: float = 0.0
# damage kick private vars
var _damage_pitch: float = 0.0
var _damage_roll: float = 0.0
var _damage_timer: float = 0.0
# weapon kick private variable
var _weapon_kick_angle: Vector3 = Vector3.ZERO
# screen shake private variables
var _screen_shake_tween: Tween
# head bob private variables
var _step_timer: float = 0.0
const MIN_SCREEN_SHAKE: float = 0.05
const MAX_SCREEN_SHAKE: float = 0.5
func _process(delta: float) -> void:
calc_view_offset(delta)
func get_camera_center():
return self.get_viewport().get_size() / 2
func calc_view_offset(delta: float):
assert(player, "player is not attached to camera effects script")
var offset = Vector3.ZERO
var angles = Vector3.ZERO
var velocity = player.velocity
if enable_tilt:
var forward = global_transform.basis.z
var right = global_transform.basis.x
var forward_dot = velocity.dot(forward)
var forward_tilt = clampf(forward_dot * deg_to_rad(run_pitch), deg_to_rad(-max_pitch), deg_to_rad(max_pitch))
var right_dot = velocity.dot(right)
var side_tilt = clampf(right_dot * deg_to_rad(run_roll), deg_to_rad(-max_roll), deg_to_rad(max_roll))
angles.z -= side_tilt
angles.x += forward_tilt
if enable_fall_kick:
_fall_timer -= delta
var fall_ratio = max(0.0, _fall_timer / fall_time)
var fall_kick_amount = fall_ratio * _fall_value
offset.y -= fall_kick_amount
angles.x -= fall_kick_amount
if enable_fall_kick:
_damage_timer -= delta
var damage_ratio = max(0.0, _damage_timer / damage_time)
angles.x += damage_ratio * _damage_pitch
angles.z += damage_ratio * _damage_roll
if enable_weapon_kick:
_weapon_kick_angle = _weapon_kick_angle.move_toward(Vector3.ZERO, weapon_decay * delta)
angles += _weapon_kick_angle
if enable_head_bob:
var speed = Vector2(velocity.x, velocity.z).length()
if speed > 0.1 and player.is_on_floor():
_step_timer += delta * (speed / bob_frequency)
_step_timer = fmod(_step_timer, 1.0)
else:
_step_timer = 0.0
var bob_sin = sin(_step_timer * 2.0 * PI) * 0.5
var pitch_delta = bob_sin * deg_to_rad(bob_pitch) * speed
var roll_delta = bob_sin * deg_to_rad(bob_roll) * speed
var height_delta = bob_sin * delta * bob_up
angles.x -= pitch_delta
angles.z -= roll_delta
offset.y += height_delta
self.position = offset
self.rotation = angles
func add_fall_kick(fall_strength: float):
_fall_value = deg_to_rad(fall_strength)
_fall_timer = fall_time
func add_damage_kick(pitch: float, roll: float, source: Vector3):
var forward = global_transform.basis.z
var right = global_transform.basis.x
var direction = global_position.direction_to(source)
var forward_dot = direction.dot(forward)
var right_dot = direction.dot(right)
_damage_pitch = deg_to_rad(pitch) * forward_dot
_damage_roll = deg_to_rad(roll) * right_dot
_damage_timer = damage_time
func add_weapon_kick(pitch: float, yaw: float, roll: float):
_weapon_kick_angle.x += deg_to_rad(pitch)
_weapon_kick_angle.y += deg_to_rad(randf_range(-yaw, yaw))
_weapon_kick_angle.z += deg_to_rad(randf_range(-roll, roll))
func add_screen_shake(amount: float, seconds: float) -> void:
if _screen_shake_tween:
_screen_shake_tween.kill()
_screen_shake_tween = create_tween()
_screen_shake_tween.tween_method(update_screen_shake.bind(amount), 0.0, 1.0, seconds).set_ease(Tween.EASE_OUT)
func update_screen_shake(alpha: float, amount: float) -> void:
amount = remap(amount, 0.0, 1.0, MIN_SCREEN_SHAKE, MAX_SCREEN_SHAKE)
var current_shake_amount = amount * (1.0 - alpha)
h_offset = randf_range(-current_shake_amount, current_shake_amount)
v_offset = randf_range(-current_shake_amount, current_shake_amount)