extends Node3D class_name CameraHolder var active : bool = true : set = set_active @export_group("Camera variables") @export_range(1.0, 179.0, 0.1) var fov_val : float = 50.0 @export_range(0.0, 0.02, 0.0001) var mouse_sensibility : float = 0.005 @export_range(0.0, 0.02, 0.0001) var joystick_sensibility : float = 0.05 @export_range(-90.0, 90.0, 0.1) var min_limit_x : float = -80.0 #in degrees here for clearer lisibility, is converted in radians when needed @export_range(-90.0, 90.0, 0.1) var max_limit_x : float = 10.0 #in degrees here for clearer lisibility, is converted in radians when needed @export_group("Zoom variables") @export var max_spring_length : float = 30.0 @export var min_spring_length : float = 3.0 @export var zoom_speed : float = 10.0 @export_group("Aim variables") var cam_aimed : bool = false #if true, cam goes into "aim/shooter mode", above the play char shoulder @export var aim_cam_pos : Vector3 = Vector3(1.4, 0.9, 0.0) var aim_cam_pos_side : bool = true #false = left, true = right @export var aim_cam_transition_speed : float = 10.0 var cam_offset : Vector3 = Vector3.ZERO #decalage actuel de la camera (interpole) @export_range(0.1, 1.0, 0.01) var aim_distance_multiplier : float = 0.6 #1.0 = meme distance que hors visee, 0.5 = moitie moins loin @export var min_cam_distance : float = 0.8 #distance minimale entre le pivot et la camera var cam_distance_mult : float = 1.0 #multiplicateur actuel (interpole) @export_group("Lock-on variables") @export var lock_on_speed : float = 8.0 var lock_target : Node3D = null #input : renseigne par InputManagementComponent, le CameraHolder ne connait aucun nom d'action var player_input : InputManagementComponent #reference variables @export_group("References") @export var spring_arm : SpringArm3D @export var cam : Camera3D func _ready() -> void: if spring_arm == null or cam == null: push_error("CameraHolder : assigne spring_arm et cam dans l'inspecteur.") return #set fov, capture mouse cursor, and enable camera cam.fov = fov_val #Input.mouse_mode = Input.MOUSE_MODE_CAPTURED Input.set_use_accumulated_input(false) set_active(active) func set_active(state : bool) -> void: #enable/disable play char camera active = state set_process_input(active) set_process(active) func _input(event: InputEvent) -> void: if event is InputEventMouseMotion: if has_lock_target(): return var viewport_transform: Transform2D = get_tree().root.get_final_transform() var mouse_motion = event.xformed_by(viewport_transform).relative rotate_from_vector(mouse_motion * mouse_sensibility) if event is InputEventMouseMotion: var viewport_transform: Transform2D = get_tree().root.get_final_transform() var mouse_motion = event.xformed_by(viewport_transform).relative rotate_from_vector(mouse_motion * mouse_sensibility) func _process(delta: float) -> void: #position the cam according to its mode (default, aim (with left or right side)) var target_offset : Vector3 = Vector3.ZERO var target_mult : float = 1.0 if cam_aimed: var side : float = 1.0 if aim_cam_pos_side else -1.0 #true = droite, false = gauche target_offset = Vector3(aim_cam_pos.x * side, aim_cam_pos.y, aim_cam_pos.z) target_mult = aim_distance_multiplier var weight : float = clampf(aim_cam_transition_speed * delta, 0.0, 1.0) cam_offset = cam_offset.lerp(target_offset, weight) cam_distance_mult = lerpf(cam_distance_mult, target_mult, weight) var distance : float = maxf(spring_arm.get_hit_length() * cam_distance_mult + cam_offset.z, min_cam_distance) cam.position = Vector3(cam_offset.x, cam_offset.y, distance) if has_lock_target(): lock_on_handling(delta) else: if player_input: rotate_from_vector(player_input.read_look_vector() * joystick_sensibility) #handle zoom zoom_handling(delta) func rotate_from_vector(vector : Vector2) -> void: #rotate cam by the vector's amount, and clamp the rotation between max up and max down values #(to avoid doing 360 degree turn with the cam for example) if vector.length() == 0: return rotation.y -= vector.x rotation.x -= vector.y rotation.x = clamp(rotation.x, deg_to_rad(min_limit_x), deg_to_rad(max_limit_x)) func zoom_handling(delta : float) -> void: #zoom in/out cam, and clamp zoom value between min and max zoom values if player_input: spring_arm.spring_length += player_input.read_zoom_axis() * zoom_speed * delta spring_arm.spring_length = clamp(spring_arm.spring_length, min_spring_length, max_spring_length) func switch_aim_side() -> void: aim_cam_pos_side = !aim_cam_pos_side func has_lock_target() -> bool: return is_instance_valid(lock_target) func lock_on_handling(delta : float) -> void: #tourne progressivement la camera vers la cible verrouillee var to_target : Vector3 = lock_target.global_position - global_position var flat_dist : float = Vector2(to_target.x, to_target.z).length() if flat_dist < 0.01: return var target_yaw : float = atan2(-to_target.x, -to_target.z) var target_pitch : float = clampf(atan2(to_target.y, flat_dist), deg_to_rad(min_limit_x), deg_to_rad(max_limit_x)) var weight : float = clampf(lock_on_speed * delta, 0.0, 1.0) rotation.y = lerp_angle(rotation.y, target_yaw, weight) rotation.x = lerp_angle(rotation.x, target_pitch, weight) func set_lock_target(t : Node3D) -> void: lock_target = t