Bewegung: - Souls-Modus (kein Ziel): Charakter dreht sich zur Laufrichtung relativ zu camera_pivot.world_yaw; bei RMB gehalten → Strafe statt Drehung - Walk-Toggle (NumLock): RPG-Strafe-Modus mit langsamerer Geschwindigkeit - Lock-On (Ziel markiert): Spieler dreht sich smooth zum Gegner, WASD = Strafe - Ausweichrolle (Shift): rollt in Eingaberichtung (world_yaw-relativ im Souls-Modus) - Sofort-180°-Snap statt animierter Drehung bei >150° Winkelunterschied Kamera (camera_pivot.gd): - world_yaw: absolute Weltausrichtung, unabhängig von Spielerrotation (kein Feedback-Loop) - LMB gehalten: Kamera orbitet, Spieler dreht sich nicht - RMB gehalten: Spieler + Kamera drehen sich gemeinsam - Soft Lock-On: camera_pivot dreht Spieler smooth zum Ziel Animationen: - Neue FBX-Animationen: Quick Roll, Running Jump, Walking Jump, Running Strafe L/R, Running Turn 180 - Animationen im Souls-Modus: immer "run" vorwärts; S = walk_back - Root-Motion-Strip: XZ-Bewegung auf Knochen-Tracks wird genullt Welt: - Boden-Shader: Schachbrettmuster in World-Space (INV_VIEW_MATRIX) - ProceduralSkyMaterial + WorldEnvironment per Code - Alte assets/animations und assets/models durch Warrior+Animation ersetzt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
76 lines
3.5 KiB
GDScript
76 lines
3.5 KiB
GDScript
# 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)
|
||
|
||
@onready var camera = $Camera3D
|
||
|
||
func _ready():
|
||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||
world_yaw = get_parent().rotation.y
|
||
|
||
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) and not has_target:
|
||
# LMB: nur Kamera dreht sich, Spieler bleibt
|
||
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) and not has_target:
|
||
# RMB: Spieler + Kamera drehen sich gemeinsam
|
||
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:
|
||
camera.position.z = clamp(camera.position.z + zoom_speed, min_zoom, max_zoom)
|
||
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||
camera.position.z = clamp(camera.position.z - zoom_speed, min_zoom, max_zoom)
|
||
|
||
func _process(delta):
|
||
var player = get_parent()
|
||
|
||
if player.target != null and is_instance_valid(player.target) and not player.is_rolling:
|
||
# Soft Lock-On: Spieler dreht sich zum Ziel, Kamera folgt direkt dahinter
|
||
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
|