2025-12-29 11:54:31 +08:00
|
|
|
|
class_name CelesteLocomotionComponent extends JumpLocomotionComponent
|
|
|
|
|
|
|
|
|
|
|
|
#region 冲刺相关参数
|
|
|
|
|
|
@export_category("Dash Properties")
|
|
|
|
|
|
##最大的冲刺次数
|
|
|
|
|
|
@export var maxium_dash_count:int = 1
|
|
|
|
|
|
##Dash时的速度
|
|
|
|
|
|
@export var dash_speed: float = 240
|
|
|
|
|
|
##结束Dash的速度
|
|
|
|
|
|
@export var end_dash_speed: float = 160
|
|
|
|
|
|
##Dash的冷却时间
|
|
|
|
|
|
@export var dash_cold_down: float = .2
|
|
|
|
|
|
##是否在组件ready时自动补充所有的Dash次数
|
|
|
|
|
|
@export var auto_recover_dash_count_at_ready: bool = true
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 攀爬相关参数
|
|
|
|
|
|
@export_category("Climb Properties")
|
|
|
|
|
|
##角色最大耐力
|
|
|
|
|
|
@export var climb_max_stamina: float
|
|
|
|
|
|
##角色向上时的速度
|
|
|
|
|
|
@export var climb_max_speed_upward: float = 45.0
|
|
|
|
|
|
##角色向下时的速度
|
|
|
|
|
|
@export var climb_max_speed_downward: float = 80.0
|
|
|
|
|
|
##角色速度变化时的最大加速度
|
|
|
|
|
|
@export var climb_acceleration: float = 900.0
|
|
|
|
|
|
##攀爬到顶点时候的自动代偿速度X
|
|
|
|
|
|
@export var climb_hop_velocity_x: float = 80
|
|
|
|
|
|
##攀爬到顶点时候的自动代偿速度Y
|
|
|
|
|
|
@export var climb_hop_velocity_y: float = 160
|
|
|
|
|
|
##攀爬跳的速度X
|
|
|
|
|
|
@export var climb_jump_velocity_x: float = 160
|
|
|
|
|
|
##攀爬跳的速度Y
|
|
|
|
|
|
@export var climb_jump_velocity_y: float = 160
|
|
|
|
|
|
##攀爬跳的最大可输入时长
|
|
|
|
|
|
@export var climb_jump_time: float = .14
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-31 16:24:11 +08:00
|
|
|
|
#region Custom Move (拉扯移動)
|
|
|
|
|
|
@export_category("Custom Move")
|
2026-01-02 18:37:09 +08:00
|
|
|
|
##進行自定義運動時,移動的拉力
|
2025-12-31 16:24:11 +08:00
|
|
|
|
@export var custom_move_force: float = 25.0
|
2026-01-02 18:37:09 +08:00
|
|
|
|
##進行自定義運動時,移動的最大速度
|
2025-12-31 16:24:11 +08:00
|
|
|
|
@export var custom_move_max_speed: float = 420.0
|
2026-01-02 18:37:09 +08:00
|
|
|
|
##進行自定義運動時,如果于目標點小於此距離,則會解除自定義運動
|
2025-12-31 16:24:11 +08:00
|
|
|
|
@export var custom_move_stop_distance: float = 4.0
|
|
|
|
|
|
|
|
|
|
|
|
var _custom_move_target_node: Node = null
|
|
|
|
|
|
var _is_custom_moving: bool = false
|
|
|
|
|
|
signal hook_released
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-29 11:54:31 +08:00
|
|
|
|
#region 冲刺field
|
|
|
|
|
|
var last_dash_count: int
|
|
|
|
|
|
##此变量为总开关,如果关闭,则在物理更新里无论如何也是不会更新Dash的,这里默认为false,因为Dash需要手动触发,最好不要常开
|
|
|
|
|
|
var can_dash: bool = false
|
|
|
|
|
|
var _dash_direction: Vector2 #存储Dash的Direction
|
|
|
|
|
|
|
|
|
|
|
|
signal dash_count_changed(count: int)
|
|
|
|
|
|
signal dash_brusted
|
|
|
|
|
|
signal dash_prepared
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 攀爬field
|
|
|
|
|
|
var can_climb: bool = false
|
|
|
|
|
|
var _climb_input: float
|
|
|
|
|
|
var _climb_hop_flag: bool = false
|
|
|
|
|
|
var _is_climb_jumping: bool = false
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-31 16:24:11 +08:00
|
|
|
|
|
2025-12-29 11:54:31 +08:00
|
|
|
|
func _init_component() -> void:
|
|
|
|
|
|
super._init_component()
|
|
|
|
|
|
|
|
|
|
|
|
if auto_recover_dash_count_at_ready:
|
|
|
|
|
|
full_recover_dash_count()
|
|
|
|
|
|
|
|
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
|
|
|
|
if Engine.is_editor_hint():
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2025-12-31 16:24:11 +08:00
|
|
|
|
if _is_custom_moving:
|
|
|
|
|
|
_update_custom_move(delta)
|
|
|
|
|
|
characterbody.move_and_slide()
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2025-12-29 11:54:31 +08:00
|
|
|
|
if can_climb:
|
|
|
|
|
|
_update_climb(delta)
|
|
|
|
|
|
characterbody.move_and_slide() #移动更新
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if can_dash:
|
|
|
|
|
|
characterbody.move_and_slide()
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
super._physics_process(delta)
|
|
|
|
|
|
|
|
|
|
|
|
func _update_climb(delta) -> void:
|
|
|
|
|
|
var target_abs: float
|
|
|
|
|
|
|
|
|
|
|
|
#更新目标速度
|
|
|
|
|
|
if sign(_climb_input) == .0:
|
|
|
|
|
|
target_abs = 0
|
|
|
|
|
|
elif sign(_climb_input) > 0:
|
|
|
|
|
|
target_abs = climb_max_speed_downward
|
|
|
|
|
|
else:
|
|
|
|
|
|
target_abs = climb_max_speed_upward
|
|
|
|
|
|
|
|
|
|
|
|
var target = target_abs * _climb_input
|
|
|
|
|
|
characterbody.velocity.y = speed_approach(characterbody.velocity.y,target,climb_acceleration * delta)
|
|
|
|
|
|
|
|
|
|
|
|
func _update_dash(delta) -> void:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2025-12-31 16:24:11 +08:00
|
|
|
|
##特殊移動
|
|
|
|
|
|
func _custom_move_to(target_node: Node) -> bool:
|
|
|
|
|
|
if target_node == null:
|
|
|
|
|
|
return false
|
|
|
|
|
|
# 要求目標至少能提供 global_position(2D 常見)
|
|
|
|
|
|
if not ("global_position" in target_node):
|
|
|
|
|
|
push_warning("custom_move target_node has no global_position.")
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
|
|
_custom_move_target_node = target_node
|
|
|
|
|
|
_is_custom_moving = true
|
|
|
|
|
|
|
|
|
|
|
|
# 進入自定義移動時,關掉其他行為(按你需求可調)
|
|
|
|
|
|
can_dash = false
|
|
|
|
|
|
can_climb = false
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
## 停止特殊移動(Custom Move)
|
|
|
|
|
|
func stop_custom_move(
|
|
|
|
|
|
force_stop_velocity: bool = true,
|
|
|
|
|
|
clear_target: bool = true
|
|
|
|
|
|
) -> void:
|
|
|
|
|
|
if not _is_custom_moving:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
_is_custom_moving = false
|
|
|
|
|
|
|
|
|
|
|
|
if clear_target:
|
|
|
|
|
|
_custom_move_target_node = null
|
|
|
|
|
|
|
|
|
|
|
|
if force_stop_velocity:
|
|
|
|
|
|
characterbody.velocity = Vector2.ZERO
|
|
|
|
|
|
|
|
|
|
|
|
##預製的保留速度的hook stop
|
|
|
|
|
|
func _release_hook_keep_momentum() -> void:
|
|
|
|
|
|
stop_custom_move(false)
|
|
|
|
|
|
hook_released.emit()
|
|
|
|
|
|
|
|
|
|
|
|
##特殊移動更新函數
|
|
|
|
|
|
func _update_custom_move(delta: float) -> void:
|
|
|
|
|
|
if not _is_custom_moving:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 目標被刪掉/釋放了
|
|
|
|
|
|
if _custom_move_target_node == null or not is_instance_valid(_custom_move_target_node):
|
|
|
|
|
|
_is_custom_moving = false
|
|
|
|
|
|
_custom_move_target_node = null
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
var target_pos: Vector2 = _custom_move_target_node.global_position
|
|
|
|
|
|
var pos: Vector2 = characterbody.global_position
|
|
|
|
|
|
var to_target := target_pos - pos
|
|
|
|
|
|
var distance := to_target.length()
|
|
|
|
|
|
|
|
|
|
|
|
if distance <= custom_move_stop_distance:
|
|
|
|
|
|
_is_custom_moving = false
|
|
|
|
|
|
_custom_move_target_node = null
|
|
|
|
|
|
characterbody.velocity = Vector2.ZERO
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
var dir := to_target / distance
|
|
|
|
|
|
|
|
|
|
|
|
# 拉扯加速度:距離越遠越快(“被拉過去”的感覺)
|
|
|
|
|
|
var acceleration := dir * distance * custom_move_force
|
|
|
|
|
|
|
|
|
|
|
|
characterbody.velocity += acceleration * delta
|
|
|
|
|
|
|
|
|
|
|
|
# 限制最大速度
|
|
|
|
|
|
var v_len := characterbody.velocity.length()
|
|
|
|
|
|
if v_len > custom_move_max_speed:
|
|
|
|
|
|
characterbody.velocity = characterbody.velocity / v_len * custom_move_max_speed
|
|
|
|
|
|
|
2025-12-29 11:54:31 +08:00
|
|
|
|
##整合之后的函数,如果不需要额外的逻辑,可以直接通过这个触发Dash
|
|
|
|
|
|
func integrate_dash(dash_dir: Vector2) -> bool:
|
|
|
|
|
|
if not predash(dash_dir):
|
|
|
|
|
|
return false
|
|
|
|
|
|
await get_tree().process_frame #等待一帧
|
|
|
|
|
|
brust_dash()
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
##dash函数,输入一个Dash的方向,返回是否成功
|
|
|
|
|
|
func brust_dash() -> bool:
|
|
|
|
|
|
var old: Vector2 = characterbody.velocity;
|
|
|
|
|
|
var burst: Vector2 = _dash_direction.normalized() * dash_speed;
|
|
|
|
|
|
|
|
|
|
|
|
if sign(old.x) == sign(burst.x) and abs(old.x) > abs(burst.x):
|
|
|
|
|
|
burst.x = old.x
|
|
|
|
|
|
|
|
|
|
|
|
characterbody.velocity = burst
|
|
|
|
|
|
dash_brusted.emit()
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
##dash准备函数,需要在dash的前一帧触发
|
|
|
|
|
|
func predash(dash_dir: Vector2) -> bool:
|
|
|
|
|
|
if not get_can_dash():
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
|
|
dash_prepared.emit()
|
|
|
|
|
|
last_dash_count = maxi(last_dash_count -1,0)
|
|
|
|
|
|
can_dash = true
|
|
|
|
|
|
|
|
|
|
|
|
_dash_direction = dash_dir
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
##停止Dash,实际上是关闭Dash标识符
|
|
|
|
|
|
func stop_dash(force_stop: bool = false) -> void:
|
|
|
|
|
|
if force_stop:
|
|
|
|
|
|
characterbody.velocity = Vector2.ZERO
|
|
|
|
|
|
characterbody.move_and_slide()
|
|
|
|
|
|
can_dash = false
|
|
|
|
|
|
|
|
|
|
|
|
##完全回复的Dash次数
|
|
|
|
|
|
func full_recover_dash_count() -> void:
|
|
|
|
|
|
dash_count_changed.emit(last_dash_count)
|
|
|
|
|
|
last_dash_count = maxium_dash_count
|
|
|
|
|
|
|
|
|
|
|
|
##回复指定数量的Dash次数
|
|
|
|
|
|
func recover_dash_count(recover_count : int) -> void:
|
|
|
|
|
|
last_dash_count = clampi(last_dash_count+ recover_count, 0,maxium_dash_count)
|
|
|
|
|
|
|
|
|
|
|
|
##子类可以重写此方法以自定义Dash的条件
|
|
|
|
|
|
func get_can_dash() -> bool:
|
|
|
|
|
|
return last_dash_count > 0
|
|
|
|
|
|
|
|
|
|
|
|
##添加攀爬的输入
|
|
|
|
|
|
func add_climb_input(input: float) -> bool:
|
|
|
|
|
|
_climb_input = input
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
##开始攀爬
|
|
|
|
|
|
func start_climb(force_reset: bool = false) -> void:
|
|
|
|
|
|
if force_reset:
|
|
|
|
|
|
characterbody.velocity = Vector2.ZERO
|
|
|
|
|
|
characterbody.move_and_slide()
|
|
|
|
|
|
|
|
|
|
|
|
can_climb = true
|
|
|
|
|
|
|
|
|
|
|
|
##终止攀爬
|
|
|
|
|
|
func suspend_climb() -> void:
|
|
|
|
|
|
can_climb = false
|
|
|
|
|
|
|
|
|
|
|
|
##攀爬补充跳跃
|
|
|
|
|
|
func climb_hop() -> void:
|
|
|
|
|
|
_climb_hop_flag = true
|
|
|
|
|
|
characterbody.velocity = Vector2(climb_hop_velocity_x * Player.get_direction_vector(characterbody.direction).x,-1 * climb_hop_velocity_y)
|
|
|
|
|
|
|
|
|
|
|
|
func start_climb_jump() -> bool:
|
|
|
|
|
|
if _is_jumping : return false
|
|
|
|
|
|
|
|
|
|
|
|
#给与一个跳跃的补正速度
|
|
|
|
|
|
characterbody.velocity.x = _movement_input * climb_jump_velocity_x # 跳跃的水平补正速度
|
|
|
|
|
|
|
|
|
|
|
|
_jump_timer = climb_jump_time
|
|
|
|
|
|
_is_jumping = true
|
|
|
|
|
|
_is_climb_jumping = true
|
|
|
|
|
|
can_climb = false #取消掉原本的climb,如果后续重新触发climb,则重新判断
|
|
|
|
|
|
characterbody.velocity.y = _get_jump_force()
|
|
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
func stop_climb_jump() -> void:
|
|
|
|
|
|
_is_jumping = false
|
|
|
|
|
|
_is_climb_jumping = false
|
|
|
|
|
|
|
|
|
|
|
|
func _get_jump_force() -> float:
|
|
|
|
|
|
if _is_climb_jumping:
|
|
|
|
|
|
return -1 * climb_jump_velocity_y
|
|
|
|
|
|
return super._get_jump_force()
|