2026-02-03 12:51:26 +08:00
|
|
|
|
class_name VinePlatform
|
|
|
|
|
|
extends StaticBody2D
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ===== 节点引用 =====
|
|
|
|
|
|
@onready var player_trigger: Area2D = %PlayerTriggerVolumn
|
|
|
|
|
|
@onready var hook_trigger: Area2D = %HookAttractVolumn
|
|
|
|
|
|
@onready var sprite: Sprite2D = $Sprite2D
|
|
|
|
|
|
@onready var collision: CollisionShape2D = $CollisionShape2D
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ===== 配置 =====
|
|
|
|
|
|
|
|
|
|
|
|
## 触发到消失的总时间(固定)
|
|
|
|
|
|
@export var destroy_total_time: float = 2.0
|
|
|
|
|
|
|
|
|
|
|
|
## 触发后多久开始抖动(可调)
|
|
|
|
|
|
@export var shake_start_time: float = 1.0
|
|
|
|
|
|
|
|
|
|
|
|
## 消失后多久恢复(再生时间)
|
|
|
|
|
|
@export var respawn_time: float = 3.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 抖动参数
|
|
|
|
|
|
@export var shake_x: float = 1.0
|
|
|
|
|
|
@export var shake_y: float = 1.0
|
|
|
|
|
|
@export var step_time: float = 0.034
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ===== 状态机 =====
|
|
|
|
|
|
enum State {
|
|
|
|
|
|
IDLE,
|
|
|
|
|
|
TRIGGERED,
|
|
|
|
|
|
DISAPPEARED
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var _current_state: State = State.IDLE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ===== 抖动控制 =====
|
|
|
|
|
|
var _shake_tween: Tween
|
|
|
|
|
|
var _shake_origin_pos: Vector2
|
|
|
|
|
|
|
|
|
|
|
|
## 防止重复触发
|
|
|
|
|
|
var _sequence_started := false
|
2026-01-24 21:14:38 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2026-02-03 12:51:26 +08:00
|
|
|
|
player_trigger.body_entered.connect(_on_player_entered)
|
|
|
|
|
|
hook_trigger.body_entered.connect(_on_hook_hit)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
## 状态切换
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
func change_state(new_state: State) -> void:
|
|
|
|
|
|
if new_state == _current_state:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
_current_state = new_state
|
|
|
|
|
|
|
|
|
|
|
|
match new_state:
|
|
|
|
|
|
State.IDLE:
|
|
|
|
|
|
_enable_platform(true)
|
|
|
|
|
|
|
|
|
|
|
|
State.TRIGGERED:
|
|
|
|
|
|
_start_destroy_sequence()
|
|
|
|
|
|
|
|
|
|
|
|
State.DISAPPEARED:
|
|
|
|
|
|
_disappear_and_respawn()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
## 触发入口
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
|
|
|
|
|
|
func _on_player_entered(body: Node) -> void:
|
|
|
|
|
|
if body is Player:
|
|
|
|
|
|
_trigger_platform()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_hook_hit(hit_pos: Vector2, hook: Hook) -> void:
|
|
|
|
|
|
_trigger_platform()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _trigger_platform() -> void:
|
|
|
|
|
|
if _sequence_started:
|
|
|
|
|
|
return
|
|
|
|
|
|
_sequence_started = true
|
|
|
|
|
|
|
|
|
|
|
|
print("藤蔓平台触发:2秒后消失,再生时间:", respawn_time)
|
|
|
|
|
|
|
|
|
|
|
|
change_state(State.TRIGGERED)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
## 销毁流程:抖动 + 消失(非销毁)
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
func _start_destroy_sequence() -> void:
|
|
|
|
|
|
var shake_time: float = clampf(shake_start_time, 0.0, destroy_total_time)
|
|
|
|
|
|
var remaining_time: float = destroy_total_time - shake_time
|
|
|
|
|
|
|
|
|
|
|
|
# 前半段静止
|
|
|
|
|
|
if shake_time > 0.0:
|
|
|
|
|
|
await get_tree().create_timer(shake_time).timeout
|
|
|
|
|
|
|
|
|
|
|
|
# 后半段抖动
|
|
|
|
|
|
if remaining_time > 0.0:
|
|
|
|
|
|
start_shaking()
|
|
|
|
|
|
await get_tree().create_timer(remaining_time).timeout
|
|
|
|
|
|
|
|
|
|
|
|
stop_shaking()
|
|
|
|
|
|
|
|
|
|
|
|
# 进入消失状态
|
|
|
|
|
|
change_state(State.DISAPPEARED)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
## 消失 + 再生
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
func _disappear_and_respawn() -> void:
|
|
|
|
|
|
print("平台消失,等待再生...")
|
|
|
|
|
|
|
|
|
|
|
|
# 关闭平台
|
|
|
|
|
|
_enable_platform(false)
|
|
|
|
|
|
|
|
|
|
|
|
# 等待再生时间
|
|
|
|
|
|
await get_tree().create_timer(respawn_time).timeout
|
|
|
|
|
|
|
|
|
|
|
|
print("平台恢复!")
|
|
|
|
|
|
|
|
|
|
|
|
# 重置触发锁
|
|
|
|
|
|
_sequence_started = false
|
|
|
|
|
|
|
|
|
|
|
|
# 回到初始状态
|
|
|
|
|
|
change_state(State.IDLE)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
## 启用/禁用平台(关键)
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
func _enable_platform(enable: bool) -> void:
|
|
|
|
|
|
sprite.visible = enable
|
|
|
|
|
|
collision.disabled = not enable
|
|
|
|
|
|
|
|
|
|
|
|
player_trigger.monitoring = enable
|
|
|
|
|
|
hook_trigger.monitoring = enable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
## 抖动效果
|
|
|
|
|
|
## ==============================
|
|
|
|
|
|
func start_shaking() -> void:
|
|
|
|
|
|
if _shake_tween and _shake_tween.is_running():
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
_shake_origin_pos = sprite.position
|
|
|
|
|
|
|
|
|
|
|
|
_shake_tween = create_tween()
|
|
|
|
|
|
_shake_tween.set_loops()
|
|
|
|
|
|
_shake_tween.set_trans(Tween.TRANS_SINE)
|
|
|
|
|
|
_shake_tween.set_ease(Tween.EASE_IN_OUT)
|
|
|
|
|
|
|
|
|
|
|
|
_shake_tween.tween_property(sprite, "position", _shake_origin_pos + Vector2(-shake_x, -shake_y), step_time)
|
|
|
|
|
|
_shake_tween.tween_property(sprite, "position", _shake_origin_pos + Vector2(shake_x, shake_y), step_time)
|
|
|
|
|
|
_shake_tween.tween_property(sprite, "position", _shake_origin_pos + Vector2(-shake_x, shake_y), step_time)
|
|
|
|
|
|
_shake_tween.tween_property(sprite, "position", _shake_origin_pos + Vector2(shake_x, -shake_y), step_time)
|
|
|
|
|
|
|
2026-01-24 21:14:38 +08:00
|
|
|
|
|
2026-02-03 12:51:26 +08:00
|
|
|
|
func stop_shaking() -> void:
|
|
|
|
|
|
if _shake_tween:
|
|
|
|
|
|
_shake_tween.kill()
|
|
|
|
|
|
_shake_tween = null
|
2026-01-24 21:14:38 +08:00
|
|
|
|
|
2026-02-03 12:51:26 +08:00
|
|
|
|
sprite.position = _shake_origin_pos
|