credit
This commit is contained in:
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
uid://xqjom65pdi52
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
uid://csjajikq0yq7g
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://p0sr27jji18b
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
uid://mnf6fljk6624
|
||||
Reference in New Issue
Block a user