xiandie/scene/ground/script/c01/s00_自动跟随的老鼠.gd

185 lines
5.7 KiB
GDScript3
Raw Normal View History

2025-01-23 06:26:57 +00:00
@tool
extends Node2D
@export var enabled := true
@export var amounts := 5
# 老鼠活动区域
@export var action_area := Vector2(200, 30):
set(val):
action_area = val
queue_redraw()
@export var gizmo_outline_color := Color(0.565, 0.271, 0.318, 0.8):
set(val):
gizmo_outline_color = val
queue_redraw()
@export var move_speed := 70.0
2025-01-23 06:26:57 +00:00
@export var distance_to_player_range := Vector2(50, 270)
@export var random_wait_time_range := Vector2(0.5, 1.0)
@export var random_wait_possibility := 0.4
@export var random_mov_distace_range := Vector2(20, 50)
@export var scatter_on_start := false
@export var debug_scatter := false:
set(val):
debug_scatter = false
if Engine.is_editor_hint():
_init_mice(true)
var mice = []
# 大于 0 时等待,小于 0 时移动abs()<0.1 时重新生成时间
var random_wait_time: Array[float] = []
var random_directions: Array[Vector2] = []
var random_positions: Array[Vector2] = []
2025-01-23 06:26:57 +00:00
var player
func _ready() -> void:
if Engine.is_editor_hint():
queue_redraw()
_init_mice()
visibility_changed.connect(_on_visibility_changed)
func _on_visibility_changed() -> void:
if visible and is_node_ready() and not Engine.is_editor_hint():
_init_mice()
2025-01-23 06:26:57 +00:00
func _enter_tree() -> void:
if is_node_ready() and not Engine.is_editor_hint():
2025-01-23 06:26:57 +00:00
_init_mice()
func flush_right_and_disable(_res = null) -> void:
2025-01-23 06:26:57 +00:00
enabled = false
var tween = create_tween()
for i in range(mice.size()):
var m = mice[i]
m.flip_h = true
if i == 0:
tween.tween_property(m, "position:x", m.position.x + 10000, 100.0)
else:
tween.parallel().tween_property(m, "position:x", m.position.x + 10000, 100.0)
func _init_mice(scatter := scatter_on_start) -> void:
mice.clear()
for c in get_children():
if c is Node2D:
2025-01-23 06:26:57 +00:00
mice.append(c)
random_wait_time.resize(mice.size())
random_directions.resize(mice.size())
random_positions.resize(mice.size())
for i in range(mice.size()):
var m = mice[i]
# clamp to action area
if scatter:
var pos = Vector2(randf() * action_area.x, randf() * action_area.y)
m.position = pos
random_positions[i] = m.position
random_directions[i] = Vector2.ONE
random_wait_time[i] = 0
func _physics_process(delta: float) -> void:
if Engine.is_editor_hint() or not enabled or not visible:
2025-01-23 06:26:57 +00:00
return
if not player:
player = SceneManager.get_player()
if not player:
return
var player_global_position = player.global_position
# clamp to action area, 允许稍微大一点,防止卡位
player_global_position.x = clampf(
player_global_position.x, global_position.x - 2, global_position.x + action_area.x + 4
)
for i in range(mice.size()):
var m = mice[i]
var m_pos = m.position
var m_global_pos = m.global_position
var squared_distance = m_global_pos.distance_squared_to(player_global_position)
var facing_right := true
var direction = m_global_pos.direction_to(player_global_position)
if squared_distance < pow(distance_to_player_range.x, 2):
# move away
facing_right = direction.x < 0
if facing_right:
m.position.x += move_speed * delta
else:
m.position.x -= move_speed * delta
m.position.y -= direction.y * move_speed * delta
random_wait_time[i] = -0.5 # 进入运动状态
random_positions[i] = m.position
elif squared_distance > pow(distance_to_player_range.y, 2):
# move closer
facing_right = direction.x > 0
if facing_right:
m.position.x += move_speed * delta
else:
m.position.x -= move_speed * delta
m.position.y += direction.y * move_speed * delta
random_wait_time[i] = -0.5 # 进入运动状态
random_positions[i] = m.position
else:
if abs(random_wait_time[i]) < 0.1:
random_wait_time[i] = (
random_wait_time_range.x
+ randf() * (random_wait_time_range.y - random_wait_time_range.x)
)
if randf() > random_wait_possibility:
random_wait_time[i] *= -1
# 一半概率等待,一半概率移动
elif random_wait_time[i] > 0:
random_wait_time[i] -= delta
# random move, 每次移动后随机生成新的目标位置,避免老鼠在原地打转
else:
random_wait_time[i] += delta
var target = random_positions[i]
if target.distance_squared_to(m_pos) < 9:
direction.y = randf_range(-0.5, 0.5)
var x_diff = player_global_position.x - m_global_pos.x
var diff_sign = signf(x_diff)
# 大概率向 distance_to_player_range 的中间移动
x_diff = (
x_diff
- (
diff_sign
* (distance_to_player_range.x + distance_to_player_range.y)
* 0.5
)
)
# 75% 的概率向 x_diff 移动
if signf(x_diff) > 0:
direction.x = 1 if randf() < 0.7 else -1
else:
direction.x = -1 if randf() < 0.7 else 1
random_directions[i] = direction
var random_dis = (
random_mov_distace_range.x
+ randf() * (random_mov_distace_range.y - random_mov_distace_range.x)
)
target = direction * random_dis + m_pos
# clamp to action area稍小于 action_area避免老鼠在边缘打转
# random_positions[i] = target.clamp(Vector2(2, 2), action_area - Vector2(4, 4))
random_positions[i] = target.clamp(Vector2.ZERO, action_area)
facing_right = target.x > m_pos.x
var delta_max = move_speed * delta
m_pos = m.position
m.position.x = move_toward(m_pos.x, target.x, delta_max)
m.position.y = move_toward(m_pos.y, target.y, delta_max)
# set flip h
m.flip_h = not facing_right
2025-01-23 06:26:57 +00:00
# clamp to action area
m.position = m.position.clamp(Vector2.ZERO, action_area)
func _draw() -> void:
if Engine.is_editor_hint():
# draw gizmo: 老鼠活动区域
var action_area_rect = Rect2(Vector2.ZERO, action_area)
# fill
var fill_color = gizmo_outline_color
fill_color.a = 0.4
draw_rect(action_area_rect, fill_color)
# outline
draw_rect(action_area_rect, gizmo_outline_color, false, 1.0)