在实现了状态机的管理之后,可以继续为其添加一个着陆的状态,让玩家在从空中着陆的时候可以有一个短暂的落地着陆动画,之后再切换到移动和其他的状态中去。
首先在状态枚举中加入着陆这一状态 LANDING:
enum State {
IDLE,
RUNNING,
JUMP,
FALL,
LANDING,
}
然后依次更改我们的状态更新函数,
tick_physics:
func tick_physics(state: State, delta: float): # 每一帧都要执行的玩家物理逻辑函数
match state:
State.IDLE:
move(default_gravity, delta)
State.RUNNING:
move(default_gravity, delta)
State.JUMP:
move(0.0 if is_first_tick else default_gravity, delta) # 如果第一帧取消重力否则下落
State.FALL:
move(default_gravity, delta)
State.LANDING:
stand(delta)
is_first_tick = false # 第一帧结束后取消第一帧的布尔
stand()函数在末尾进行解释。
get_next_state:
func get_next_state(state: State) ->State: # 根据输入获得下一个状态
var can_jump := is_on_floor() or coyote_timer.time_left > 0 # 在地面上或者处在coyoteTimer的时间内就允许跳跃
var should_jump := can_jump and jump_request_timer.time_left > 0 # 允许跳跃并且已经按下了跳跃键就应该跳跃
if should_jump: # 应该跳跃进入跳跃状态
return State.JUMP
############### 以下为不应该跳跃的情况 #########################
var direction := Input.get_axis("move_left", "move_right") # 获取水平输入
var is_still := is_zero_approx(direction) and is_zero_approx(velocity.x) # 如果水平静止
match state:
State.IDLE: # 在静止状态中
if not is_on_floor():
return State.FALL # 不在地面进入坠落状态
if not is_still:
return State.RUNNING # 不静止进入跑步状态
State.RUNNING: # 在跑步状态中
if is_still: # 回到静止就进入待机状态
return State.IDLE
if not is_on_floor(): # 不在地面就进入坠落状态
return State.FALL
State.JUMP: # 在跳跃状态中判断下一次状态应该的值
if velocity.y >= 0: # 如果速度变为向下,进入坠落状态
return State.FALL
State.FALL: # 在坠落状态中
if is_on_floor(): # 坠落回地面进入跑步或静止状态
return State.LANDING
State.LANDING:
if ! animation_player.is_playing():
return State.IDLE
return state # 最后返回得到的状态
transition_state:
func transition_state(from: State, to: State) -> void:
if from not in GROUND_STATE and to in GROUND_STATE:
### 如果是从陆地以外的状态进入到陆地状态就停止胶囊时间CoyoteTimer ###
coyote_timer.stop()
match to: # 根据目标状态更新当前的属性
State.IDLE: # 如果要去到 IDLE 状态就切换回 IDLE 应有的动画
animation_player.play("idle")
State.RUNNING: # 如果需要进入 RUNNING 就切换到 running 的动画
animation_player.play("running")
State.JUMP: # 进入到 JUMP 切换到 jump 动画并且给一个向上的初速度
animation_player.play("jump")
velocity.y = JUMP_VELOCITY
coyote_timer.stop() # 停止胶囊时间
jump_request_timer.stop() # 停止跳跃延迟时间
State.FALL: # 需要进入坠落状态播放坠落动画
animation_player.play("fall")
if from in GROUND_STATE: # 如果是从地面状态进入到坠落状态就启动胶囊时间
coyote_timer.start()
State.LANDING:
animation_player.play("landing")
is_first_tick = true # 设置当前是第一帧
##########################################################################
# 当进入一个状态时运行完一次后设置为是第一帧,并在物理函数中运动过一次后设置为是第一帧 #
##########################################################################
stand():
由于我不希望玩家在落地动画播放的时候还可以滑动,所以在着陆期间只允许站立 instead of slide!
func stand(delta: float) -> void:
var acceleration := FLOOR_ACCELERATION if is_on_floor() else AIR_ACCELERATION
velocity.x = move_toward(velocity.x, 0.0, acceleration * delta)
velocity.y += default_gravity * delta
move_and_slide()