188 lines
5.8 KiB
GDScript3
188 lines
5.8 KiB
GDScript3
|
@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()
|
|||
|
@export var move_speed := 70.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()
|
|||
|
visibility_changed.connect(_on_visibility_changed)
|
|||
|
if Engine.is_editor_hint():
|
|||
|
return
|
|||
|
# 自动跟随的老鼠 ~ c02_mouse_follow_player
|
|||
|
visible = ArchiveManager.get_global_value("c02_mouse_follow_player", false)
|
|||
|
|
|||
|
|
|||
|
func _on_visibility_changed() -> void:
|
|||
|
if visible and is_node_ready() and not Engine.is_editor_hint():
|
|||
|
_init_mice()
|
|||
|
|
|||
|
|
|||
|
func _enter_tree() -> void:
|
|||
|
if is_node_ready() and not Engine.is_editor_hint():
|
|||
|
_init_mice()
|
|||
|
|
|||
|
|
|||
|
func flush_right_and_disable(_res = null) -> 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 Node2D:
|
|||
|
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:
|
|||
|
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
|
|||
|
# 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)
|