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
+67
View File
@@ -0,0 +1,67 @@
extends CharacterBody3D
@onready var anim_player: AnimationPlayer = $cow/AnimationPlayer
@export var dest_array:Array[Marker3D]
@onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D
@onready var idle_timer: Timer = $Timer
enum NPCSTATE {
IDLE,
WALKING
}
var cur_state : NPCSTATE
var cur_destination: Marker3D
var speed = 4
@onready var audio_moo: AudioStreamPlayer3D = $AudioMoo
@onready var moo_timer: Timer = $MooTimer
var one_time_each : bool = false
func _ready() -> void:
_set_state( NPCSTATE.IDLE )
func _on_timer_timeout() -> void:
decide_next_state()
func _set_state(new_state:NPCSTATE):
cur_state = new_state
match cur_state:
NPCSTATE.IDLE:
idle_timer.start( randf_range(3, 5) )
anim_player.play("metarig|metarig|walking", -1, .7 )
NPCSTATE.WALKING:
anim_player.play("metarig|metarig|walking", -1, 1.7 )
func _physics_process(delta: float) -> void:
match cur_state:
NPCSTATE.IDLE:
pass
NPCSTATE.WALKING:
var next_path_pos:Vector3 = navigation_agent.get_next_path_position()
var new_vel : Vector3 = global_position.direction_to( next_path_pos ) * speed
velocity = new_vel
var look_at_target : Vector3 = Vector3( next_path_pos.x, global_position.y, next_path_pos.z )
if not global_position.is_equal_approx( look_at_target ):
look_at( look_at_target )
move_and_slide()
func decide_next_state():
if cur_state == NPCSTATE.IDLE:
cur_destination = dest_array.pick_random()
navigation_agent.target_position = cur_destination.global_position
_set_state( NPCSTATE.WALKING )
func _on_navigation_agent_3d_navigation_finished() -> void:
_set_state( NPCSTATE.IDLE )
func _on_moo_timer_timeout() -> void:
moo_timer.wait_time = randf_range(40, 60)
audio_moo.pitch_scale = randf_range( .8, 1.2 )
audio_moo.play()
+1
View File
@@ -0,0 +1 @@
uid://xqjom65pdi52
+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()
+1
View File
@@ -0,0 +1 @@
uid://csjajikq0yq7g
+67
View File
@@ -0,0 +1,67 @@
@tool
extends MeshInstance3D
## Dessine un rectangle (contour) représentant une zone de confinement.
## À attacher sur un MeshInstance3D vide, placé n'importe où dans la scène.
## Utilise les mêmes valeurs (zone_center / zone_size) que FleeingAnimal.gd.
@export var zone_center: Vector3 = Vector3.ZERO:
set(value):
zone_center = value
_update_rectangle()
@export var zone_size: Vector2 = Vector2(30.0, 30.0): # largeur (x) et profondeur (z)
set(value):
zone_size = value
_update_rectangle()
@export var zone_rotation_degrees: float = 0.0: # rotation du rectangle autour de l'axe Y
set(value):
zone_rotation_degrees = value
_update_rectangle()
@export var line_color: Color = Color(1.0, 0.2, 0.2):
set(value):
line_color = value
_update_material()
@export var height_offset: float = 0.05 # légère élévation pour éviter le z-fighting avec le sol
func _ready() -> void:
_update_rectangle()
_update_material()
func _update_rectangle() -> void:
var immediate := ImmediateMesh.new()
immediate.surface_begin(Mesh.PRIMITIVE_LINE_STRIP)
var half_width := zone_size.x * 0.5
var half_depth := zone_size.y * 0.5
var rot_rad := deg_to_rad(zone_rotation_degrees)
var local_corners := [
Vector3(-half_width, 0.0, -half_depth),
Vector3(half_width, 0.0, -half_depth),
Vector3(half_width, 0.0, half_depth),
Vector3(-half_width, 0.0, half_depth),
Vector3(-half_width, 0.0, -half_depth),
]
for corner in local_corners:
var rotated_corner: Vector3 = corner.rotated(Vector3.UP, rot_rad)
var final_point := zone_center + rotated_corner
final_point.y += height_offset
immediate.surface_add_vertex(final_point)
immediate.surface_end()
mesh = immediate
func _update_material() -> void:
var mat := StandardMaterial3D.new()
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
mat.albedo_color = line_color
mat.vertex_color_use_as_albedo = false
material_override = mat
+1
View File
@@ -0,0 +1 @@
uid://p0sr27jji18b
+9
View File
@@ -0,0 +1,9 @@
extends Node
@onready var audio_snort: AudioStreamPlayer3D = $AudioStreamPlayer3D
@onready var snort_timer: Timer = $SnortTimer
func _on_snort_timer_timeout() -> void:
snort_timer.wait_time = randf_range(25, 35)
audio_snort.pitch_scale = randf_range( .9, 1.1 )
audio_snort.play()
+1
View File
@@ -0,0 +1 @@
uid://mnf6fljk6624