118 lines
2.5 KiB
GDScript
118 lines
2.5 KiB
GDScript
class_name Rock
|
|
extends CharacterBody2D
|
|
|
|
## ===== 配置 =====
|
|
@export var falling_gravity: float = 2000.0
|
|
@export var max_fall_speed: float = 3000.0
|
|
@export var kill_y: float = 10000000.0
|
|
|
|
@export var shake_x := 2.0
|
|
@export var shake_y := 2.0
|
|
@export var step_time := 0.034
|
|
|
|
@export var binded_rock_texture: Node2D
|
|
|
|
## ===== 常量 =====
|
|
const NO_TARGET_Y: float = 1e20
|
|
|
|
## ===== 状态 =====
|
|
var is_falling: bool = false
|
|
var fall_target_y: float = NO_TARGET_Y
|
|
|
|
## ===== 抖动量 =====
|
|
var _shake_tween: Tween
|
|
var _shake_origin_pos: Vector2
|
|
|
|
##岩石开始抖动
|
|
func start_shaking() -> void:
|
|
var sprite := $Sprite2D
|
|
|
|
if binded_rock_texture: sprite = binded_rock_texture
|
|
if not sprite:
|
|
return
|
|
|
|
# 防止重复启动
|
|
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
|
|
)
|
|
|
|
##岩石结束抖动
|
|
func stop_shaking() -> void:
|
|
var sprite := $Sprite2D
|
|
if not sprite:
|
|
return
|
|
|
|
if _shake_tween:
|
|
_shake_tween.kill()
|
|
_shake_tween = null
|
|
|
|
# 强制回到原位,防止残留偏移
|
|
sprite.position = _shake_origin_pos
|
|
|
|
## 开始下落(可指定目标)
|
|
func start_falling(target_y: float = NO_TARGET_Y) -> void:
|
|
is_falling = true
|
|
fall_target_y = target_y
|
|
velocity = Vector2.ZERO
|
|
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
if not is_falling:
|
|
return
|
|
|
|
_update_fall(delta)
|
|
|
|
|
|
func _update_fall(delta: float) -> void:
|
|
## 速度积分
|
|
velocity.y += falling_gravity * delta
|
|
velocity.y = min(velocity.y, max_fall_speed)
|
|
|
|
## 预测下一帧位置
|
|
var next_y: float = global_position.y + velocity.y * delta
|
|
|
|
## 到达目标 / kill
|
|
var limit_y: float = min(fall_target_y, kill_y)
|
|
if next_y >= limit_y:
|
|
global_position.y = limit_y
|
|
_on_fall_finished()
|
|
return
|
|
|
|
move_and_slide()
|
|
|
|
func _on_fall_finished() -> void:
|
|
is_falling = false
|
|
queue_free() # 或者变成平台 / 播动画
|