# CameraPivot.gd # ───────────────────────────────────────────────────────────────────────────── # Third-Person Kamerasystem – Kind-Node des Spielers # # Kamera-Modi: # • Kein Ziel, LMB gehalten → Kamera orbitet um den Spieler (world_yaw ändert sich, # Spielerrotation bleibt) # • Kein Ziel, RMB gehalten → Spieler + Kamera drehen sich gemeinsam # • Ziel markiert → Soft Lock-On: Spieler dreht sich smooth zum Ziel, # Kamera bleibt direkt dahinter # • Mausrad → Zoom (min_zoom … max_zoom) # # world_yaw: absolute Weltausrichtung der Kamera in Grad (Y-Achse). # Unabhängig von player.rotation.y → verhindert Feedback-Loop bei Souls-Rotation. # camera_pivot.rotation.y wird in _process() immer als (world_yaw - player.rotation.y) # gesetzt, sodass die Kamera in World-Space stabil bleibt. # ───────────────────────────────────────────────────────────────────────────── extends Node3D @export var sensitivity = 0.3 @export var min_pitch = -40.0 @export var max_pitch = 20.0 @export var min_zoom = 5.0 @export var max_zoom = 20.0 @export var zoom_speed = 1.0 @export var lock_on_speed = 5.0 var pitch: float = 0.0 var world_yaw: float = 0.0 # Absolute Weltausrichtung der Kamera (unabhängig von Spielerrotation) var desired_zoom: float = 10.0 # Gewünschter Zoom-Abstand @onready var camera = $Camera3D func _ready(): Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) world_yaw = get_parent().rotation.y desired_zoom = camera.position.z func _input(event): var player = get_parent() var has_target = player.target != null and is_instance_valid(player.target) if event is InputEventMouseMotion: if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): # LMB: nur Kamera dreht sich, Spieler bleibt (auch mit Target → umschauen) world_yaw -= deg_to_rad(event.relative.x * sensitivity) pitch -= event.relative.y * sensitivity pitch = clamp(pitch, min_pitch, max_pitch) rotation_degrees.x = pitch elif Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT): # RMB: Spieler + Kamera drehen sich gemeinsam (auch mit Target → weglaufen) var delta_yaw = deg_to_rad(-event.relative.x * sensitivity) world_yaw += delta_yaw player.rotation.y += delta_yaw pitch -= event.relative.y * sensitivity pitch = clamp(pitch, min_pitch, max_pitch) rotation_degrees.x = pitch if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_WHEEL_DOWN: desired_zoom = clamp(desired_zoom + zoom_speed, min_zoom, max_zoom) if event.button_index == MOUSE_BUTTON_WHEEL_UP: desired_zoom = clamp(desired_zoom - zoom_speed, min_zoom, max_zoom) func _process(delta): var player = get_parent() var rmb_held = Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT) var lmb_held = Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) if player.target != null and is_instance_valid(player.target) and not player.is_rolling and not rmb_held and not lmb_held: # Soft Lock-On: Spieler dreht sich zum Ziel, Kamera folgt direkt dahinter # (RMB gehalten → Lock-On pausiert, Spieler kann sich wegdrehen) var to_target = player.target.global_position - player.global_position to_target.y = 0 if to_target.length() > 0.1: var target_angle = atan2(-to_target.x, -to_target.z) player.rotation.y = lerp_angle(player.rotation.y, target_angle, delta * lock_on_speed) world_yaw = player.rotation.y # Lokale Rotation so setzen dass Kamera immer auf world_yaw zeigt rotation.y = world_yaw - player.rotation.y # Kamera-Kollision: Raycast vom Pivot zur gewünschten Kameraposition var space_state = get_world_3d().direct_space_state var pivot_pos = global_position # Kamera sitzt auf lokaler Z-Achse des Pivots var desired_cam_local = Vector3(0, 0, desired_zoom) var desired_cam_pos = global_transform * desired_cam_local var query = PhysicsRayQueryParameters3D.create(pivot_pos, desired_cam_pos) query.exclude = [player.get_rid()] query.collide_with_bodies = true query.collide_with_areas = false query.collision_mask = 1 var result = space_state.intersect_ray(query) if result and not result.collider is CharacterBody3D: var hit_dist = pivot_pos.distance_to(result.position) - 0.5 camera.position.z = clamp(hit_dist, 1.0, desired_zoom) else: camera.position.z = lerp(camera.position.z, desired_zoom, delta * 5.0)