- Enemy: Neues castle_guard_01 Modell mit Animationen (idle, walk, run, autoattack, death, turn) - Enemy: Patrol-KI mit Turn-Animationen beim Richtungswechsel, 5s idle nach Spawn - Enemy: Aggro durch Detection Range (15m) und Schadens-Aggro, Patrol→Chase Übergang - Enemy: Respawn nach 5s am Spawnpunkt, XP-Vergabe beim Tod - Kamera: LMB frei drehen (umschauen) auch mit markiertem Ziel - Kamera: RMB Lock-On temporär aufheben zum Weglaufen - Kamera: LMB-Klick auf freie Fläche visiert Ziel ab - Kamera: Drag vs Klick Unterscheidung (< 5px Bewegung = Klick) - Autoattack greift automatisch wieder an wenn Ziel zurück in Range - Player zur Gruppe "player" hinzugefügt für Enemy-Detection - Dokumentation vollständig aktualisiert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
79 lines
3.7 KiB
GDScript
79 lines
3.7 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):
|
||
# 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:
|
||
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()
|
||
|
||
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
|