@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 := 100.0 @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] = [] var player func _ready() -> void: if Engine.is_editor_hint(): queue_redraw() _init_mice() func _enter_tree() -> void: if is_node_ready(): _init_mice() func flush_right_and_disable() -> void: 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 AnimatedSprite2D: 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: 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 = facing_right # 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)