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

185 lines
5.7 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@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()
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()
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)