2025-01-23 06:26:57 +00:00
|
|
|
|
@tool
|
|
|
|
|
extends Node2D
|
|
|
|
|
|
|
|
|
|
@export var enabled := true
|
|
|
|
|
# 老鼠活动区域
|
|
|
|
|
@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()
|
2025-03-18 14:19:39 +00:00
|
|
|
|
@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 时重新生成时间
|
2025-01-23 13:13:40 +00:00
|
|
|
|
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()
|
2025-02-12 07:03:41 +00:00
|
|
|
|
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:
|
2025-02-12 07:03:41 +00:00
|
|
|
|
if is_node_ready() and not Engine.is_editor_hint():
|
2025-01-23 06:26:57 +00:00
|
|
|
|
_init_mice()
|
|
|
|
|
|
|
|
|
|
|
2025-02-19 13:30:29 +00:00
|
|
|
|
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():
|
2025-03-10 12:57:20 +00:00
|
|
|
|
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:
|
2025-02-06 12:19:45 +00:00
|
|
|
|
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
|
2025-03-10 12:57:20 +00:00
|
|
|
|
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)
|