credit
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user