This commit is contained in:
E250429F
2026-09-17 23:19:43 +02:00
parent 025a866eef
commit 3e857fa9f3
281 changed files with 38006 additions and 970 deletions
+157
View File
@@ -0,0 +1,157 @@
extends CharacterBody3D
## --- Paramètres à régler dans l'inspecteur ---
@export var player: Node3D # Référence au joueur (à assigner dans l'inspecteur ou via groupe)
@export var flee_enabled: bool = false # Si false, l'animal ne fuit jamais, il se balade juste dans sa zone
@export var move_speed: float = 4.8
@export var acceleration: float = 12
@export var rotation_speed: float = 5
@export_group("Zone de confinement")
@export var zone_center: Vector3 = Vector3.ZERO # Centre de la zone (en coordonnées globales)
@export var zone_size: Vector2 = Vector2(30.0, 30.0) # Largeur (x) et profondeur (z) du rectangle
@export var zone_rotation_degrees: float = 0.0 # Rotation du rectangle autour de l'axe Y
@export var zone_margin: float = 1.5 # Marge de sécurité avant le bord
@export_group("Comportement idle")
@export var wander_enabled: bool = true
@export var wander_radius: float = 5.0
@export var wander_interval: float = 3.0
var _wander_target: Vector3
var _wander_timer: float = 0.0
var _state: String = "idle" # "idle" ou "flee"
# --- NOUVEAU : Variables pour gérer le déblocage sur les bords ---
var _stuck_timer: float = 0.0
var _sidestep_direction: float = 1.0 # 1.0 pour droite, -1.0 pour gauche
@onready var anim_player: AnimationPlayer = $foxes/AnimationPlayer
func _ready() -> void:
_wander_target = global_position
if player == null:
var players := get_tree().get_nodes_in_group("player")
if players.size() > 0:
player = players[0]
func _physics_process(delta: float) -> void:
if not player == null:
# --- Détermine l'état ---
if flee_enabled:
_state = "flee"
else:
_state = "idle"
var desired_direction: Vector3
if _state == "flee":
desired_direction = _get_flee_direction(delta)
else:
desired_direction = _get_wander_direction(delta)
_apply_movement(desired_direction, delta)
_update_animation()
func _get_flee_direction(delta: float) -> Vector3:
# Direction opposée au joueur (sur le plan horizontal)
var away := (global_position - player.global_position)
away.y = 0.0
away = away.normalized()
var target_pos := global_position + away * 5.0
var clamped_target = _clamp_to_zone(target_pos)
# Détection de blocage : si la cible calculée est quasiment la même que notre position
# ou si on est bloqué contre un bord (la cible clampée diffère fortement de la cible brute)
if target_pos.distance_to(clamped_target) > 0.5:
_stuck_timer += delta
if _stuck_timer > 0.3: # Si bloqué pendant plus de 0.3 secondes
# On choisit aléatoirement ou on alterne gauche/droite pour longer le bord
if randf() < 0.02:
_sidestep_direction *= -1.0
# On décale la fuite perpendiculairement (à gauche ou à droite de la fuite)
var perpendicular = Vector3(-away.z, 0.0, away.x) * _sidestep_target_sign()
away = (away + perpendicular).normalized()
clamped_target = _clamp_to_zone(global_position + away * 5.0)
else:
_stuck_timer = 0.0
return (clamped_target - global_position).normalized()
func _sidestep_target_sign() -> float:
return _sidestep_direction
func _get_wander_direction(delta: float) -> Vector3:
if not wander_enabled:
return Vector3.ZERO
_wander_timer -= delta
if _wander_timer <= 0.0 or global_position.distance_to(_wander_target) < 0.5:
_wander_timer = wander_interval
_wander_target = _pick_random_point_in_zone()
var dir := (_wander_target - global_position)
dir.y = 0.0
if dir.length() < 0.1:
return Vector3.ZERO
return dir.normalized()
func _pick_random_point_in_zone() -> Vector3:
var angle := randf() * TAU
var radius := randf() * wander_radius
var offset := Vector3(cos(angle), 0.0, sin(angle)) * radius
var point := global_position + offset
return _clamp_to_zone(point)
func _clamp_to_zone(point: Vector3) -> Vector3:
var half_width := (zone_size.x * 0.5) - zone_margin
var half_depth := (zone_size.y * 0.5) - zone_margin
var rot_rad := deg_to_rad(zone_rotation_degrees)
var local_point := (point - zone_center).rotated(Vector3.UP, -rot_rad)
local_point.x = clamp(local_point.x, -half_width, half_width)
local_point.z = clamp(local_point.z, -half_depth, half_depth)
return zone_center + local_point.rotated(Vector3.UP, rot_rad)
func _apply_movement(direction: Vector3, delta: float) -> void:
var speed := move_speed
if _state == "flee":
speed *= 1.6
var target_velocity := direction * speed
target_velocity.y = velocity.y
velocity.x = move_toward(velocity.x, target_velocity.x, acceleration * delta)
velocity.z = move_toward(velocity.z, target_velocity.z, acceleration * delta)
if not is_on_floor():
velocity.y -= 9.8 * delta
if direction.length() > 0.1:
var target_angle := atan2(direction.x, direction.z)
rotation.y = lerp_angle(rotation.y, target_angle, rotation_speed * delta)
move_and_slide()
func _update_animation() -> void:
if anim_player.current_animation != "dog_rig_Canter":
anim_player.play("dog_rig_Canter")
func _on_area_3d_body_entered(body: Node3D) -> void:
if body.is_in_group("player"):
#if body.can_run:
body.fox_grabbed += 1
queue_free()