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() # 或者变成平台 / 播动画