This commit is contained in:
Team3
2026-09-16 12:08:04 +02:00
parent 936773c4e9
commit 89fad5dc7a
12 changed files with 493 additions and 167 deletions

View File

@@ -9,8 +9,9 @@ const SPEED_TRAUM := 8.0
const SPEED_GEIST := 11.0
const GRAVITY := 24.0
const JUMP := 9.0
const FOLGE_ABSTAND := 2.2
const FOLGE_SPIEL := 1.5
const FOLGE_ABSTAND := 2.5 # so weit bleibt der Geist auf der Spur zurück
const LEINE := 14.0 # weiter weg reißt die Leine: er wird nachgezogen
const SCHWEBE := 0.25 # eine Handbreit über dem Boden
const PITCH := -0.96 # 55° von oben, fest wie in Diablo
const CAM_YAW := PI * 0.25 # Blick diagonal über den Bezirk
const ZOOM := [26.0, 40.0] # zwei Stufen, Mausrad
@@ -27,7 +28,6 @@ var arm: Node3D
var cam: Camera3D
var _dreh_rest := 0.0 # noch zu drehender Winkel (Q)
var _zoom_stufe := 0
var _schwebe_ziel := -1.0 # Etage, zu der der Geist gerade gleitet
var body: Node3D
var _schleier: MeshInstance3D
var _kern: MeshInstance3D
@@ -51,10 +51,10 @@ const FUNKEN_MAX := 6
func _ready() -> void:
var cs := CollisionShape3D.new()
var shape := CapsuleShape3D.new()
shape.radius = 0.45
shape.height = 1.9
shape.radius = 0.4
shape.height = 1.6
cs.shape = shape
cs.position.y = 0.95
cs.position.y = 0.8
add_child(cs)
body = Node3D.new()
@@ -107,18 +107,15 @@ func _ready() -> void:
func set_mode(m: int) -> void:
mode = m
folgt = null
collision_layer = 1
collision_mask = 1
if m == Mode.GEIST:
collision_layer = 0
collision_mask = 0
body.scale = Vector3.ONE * 0.6
_schleier_mat.albedo_color.a = 0.3
else:
collision_layer = 1
collision_mask = 1
body.scale = Vector3.ONE * 0.8
_schleier_mat.albedo_color.a = 0.55
velocity = Vector3.ZERO
_schwebe_ziel = -1.0
func _unhandled_input(event: InputEvent) -> void:
@@ -157,56 +154,16 @@ func _physics_process(delta: float) -> void:
elif dir.length() > 0.01:
folgt = null # eigene Bewegung löst die Verfolgung
else:
var rechts := Basis(Vector3.UP, yaw) * Vector3(1, 0, 0)
var schulter := folgt.global_position + Vector3(0, 1.4, 0) \
+ rechts * FOLGE_ABSTAND - blickrichtung() * 0.8
global_position = global_position.lerp(schulter, 1.0 - pow(0.004, delta))
# weich nachziehen, aber an der Leine: sonst reißt der Faden beim Gehen
var ab := global_position - schulter
if ab.length() > FOLGE_SPIEL:
global_position = schulter + ab.normalized() * FOLGE_SPIEL
velocity = Vector3.ZERO
body.rotation.y = lerp_angle(body.rotation.y, yaw, 1.0 - pow(0.001, delta))
_an_der_leine(delta)
return
if mode == Mode.GEIST:
var v := dir * SPEED_GEIST
velocity.x = lerpf(velocity.x, v.x, 1.0 - pow(0.001, delta))
velocity.z = lerpf(velocity.z, v.z, 1.0 - pow(0.001, delta))
# Steigen und Sinken wechseln die Etage: der Geist gleitet ein Stockwerk
if look_enabled:
var von: float = _schwebe_ziel if _schwebe_ziel >= 0.0 else global_position.y
if Input.is_action_just_pressed("steigen"):
_schwebe_ziel = clampf(von + Orte.ETAGE, 0.4, 45.0)
if Input.is_action_just_pressed("sinken"):
_schwebe_ziel = clampf(von - Orte.ETAGE, 0.4, 45.0)
if _schwebe_ziel >= 0.0:
var dy := _schwebe_ziel - global_position.y
velocity.y = clampf(dy * 6.0, -SPEED_GEIST, SPEED_GEIST)
if absf(dy) < 0.05:
global_position.y = _schwebe_ziel
velocity.y = 0.0
_schwebe_ziel = -1.0
else:
velocity.y = 0.0
move_and_slide()
if global_position.y < 0.4:
global_position.y = 0.4
if global_position.y > 45.0:
global_position.y = 45.0
else:
velocity.x = lerpf(velocity.x, dir.x * SPEED_TRAUM, 1.0 - pow(0.0005, delta))
velocity.z = lerpf(velocity.z, dir.z * SPEED_TRAUM, 1.0 - pow(0.0005, delta))
if is_on_floor():
if look_enabled and Input.is_action_pressed("steigen"):
velocity.y = JUMP
else:
velocity.y = -1.0
else:
velocity.y -= GRAVITY * delta
move_and_slide()
if global_position.y < -400.0:
global_position.y = 2.0
var tempo := SPEED_GEIST if mode == Mode.GEIST else SPEED_TRAUM
velocity.x = lerpf(velocity.x, dir.x * tempo, 1.0 - pow(0.0005, delta))
velocity.z = lerpf(velocity.z, dir.z * tempo, 1.0 - pow(0.0005, delta))
_fallen(delta)
move_and_slide()
if global_position.y < -400.0:
global_position.y = 2.0
if dir.length() > 0.01:
var ziel := atan2(-dir.x, -dir.z)
@@ -217,6 +174,40 @@ func blickrichtung() -> Vector3:
return -Vector3(sin(yaw), 0, cos(yaw))
## Schwerkraft in beiden Welten. Springen kann nur der Läufer im Traum.
func _fallen(delta: float) -> void:
if is_on_floor():
if mode == Mode.TRAUM and look_enabled and Input.is_action_pressed("steigen"):
velocity.y = JUMP
else:
velocity.y = -1.0
else:
velocity.y -= GRAVITY * delta
## Folgen heißt: seiner Spur nachgehen, durch dieselben Türen und Treppen.
## Er läuft so schnell wie nötig, um dranzubleiben -- auch bei hohem Tempo.
func _an_der_leine(delta: float) -> void:
var mensch := folgt as Bewohner
var ziel: Vector3 = mensch.spur_punkt(FOLGE_ABSTAND)
var d := ziel - global_position
d.y = 0.0
var tempo := maxf(SPEED_GEIST, Bewohner.GEHTEMPO * Game.zeit_faktor() * 1.4)
if d.length() > 0.3:
var v := d.normalized() * minf(tempo, d.length() / delta)
velocity.x = v.x
velocity.z = v.z
body.rotation.y = lerp_angle(body.rotation.y, atan2(-d.x, -d.z), 1.0 - pow(0.001, delta))
else:
velocity.x = 0.0
velocity.z = 0.0
_fallen(delta)
move_and_slide()
if global_position.distance_to(mensch.global_position) > LEINE:
global_position = ziel # hängen geblieben: die Leine zieht ihn nach
velocity = Vector3.ZERO
## Schweben, Funken, Schweif und Modusfarbe -- der Zustand ist am Geist ablesbar.
func _beleben(delta: float) -> void:
var t := Time.get_ticks_msec() * 0.001
@@ -233,7 +224,7 @@ func _beleben(delta: float) -> void:
_kopf_mat.albedo_color = _farbe.lightened(0.4)
_kopf_mat.emission = _farbe.lightened(0.4)
body.position.y = sin(t * 1.6) * 0.08
body.position.y = (SCHWEBE if mode == Mode.GEIST else 0.0) + sin(t * 1.6) * 0.08
_kern.scale = Vector3.ONE * (1.0 + sin(t * 3.1) * 0.12)
var tempo_flach := Vector2(velocity.x, velocity.z).length()
var neigung := -0.16 * clampf(tempo_flach / SPEED_TRAUM, 0.0, 1.0)