概述
当我们初学时,很多教程都教授我们使用类似is_jumping、is_air之类的布尔类型的变量表示玩家是否处于某个状态,然后在_process()或_physics_process()中每帧去侦测按键输入,然后再根据输入去改变上面的状态变量或通过判断执行相应的行为。
然而当使用这种简单粗暴的方式,为了实现玩家不同的状态和行为,我们或许会声明十几二十个这样的变量,并用各种if语句和嵌套的if语句把自己的代码搞得一团乱麻。
为此,我们引入新的方式来重构我们的代码。
原始代码
为了比较效果,这里我们还是贴上上一节基础平台移动和跳跃的代码。
![]() |
![]() |
|---|---|
原始代码
extends KinematicBody2Dvar speed = 60.0 # 速度 像素/秒var velocity = Vector2.ZERO # 速度向量var gravity = 3000 # 重力加速度var jump_force = 3000.0 # 单次跳跃高度var is_jumping = falsefunc _process(delta):velocity = Vector2.ZERO# 左右移动velocity.x = Input.get_axis("ui_left","ui_right") * speed# 实现下落velocity.y += gravity * delta# 落地if is_on_floor() and is_jumping == true:is_jumping = falsereturn# 跳跃if Input.is_action_just_pressed("ui_accept"):if is_on_floor(): # 在地面velocity.y = -jump_forceis_jumping = trueelse: # 在空中if is_jumping: # 正在跳跃# 二级跳velocity.y = -jump_forceis_jumping = falsevelocity = move_and_slide(velocity,Vector2.UP)
重构代码
然后我们对上面的代码进行重构。
![]() |
![]() |
|---|---|
原始代码
extends KinematicBody2Dvar speed = 60.0 # 速度 像素/秒var velocity = Vector2.ZERO # 速度向量var gravity = 3000 # 重力加速度var jump_force = 3000.0 # 单次跳跃高度var is_jumping = falseenum states {ON_FLOOR, # 在地面上IN_AIR # 在空中}var _state = states.ON_FLOORfunc _physics_process(delta):velocity = Vector2.ZEROmatch _state:# 在地面states.ON_FLOOR:# 边缘掉落if not is_on_floor():change_state_to(states.ON_FLOOR)is_jumping = falsereturn# 左右移动velocity.x = Input.get_axis("ui_left","ui_right") * speed# 实现下落 -- 用于边缘下落velocity.y += gravity * delta# 跳跃if Input.is_action_just_pressed("ui_accept"):velocity.y = -jump_forcechange_state_to(states.IN_AIR)is_jumping = truevelocity = move_and_slide(velocity,Vector2.UP)# 在空中states.IN_AIR:# 掉到地板if is_on_floor():change_state_to(states.ON_FLOOR)is_jumping = falsereturn# 左右移动velocity.x = Input.get_axis("ui_left","ui_right") * speed# 实现下落 -- 用于边缘下落velocity.y += gravity * delta# 跳跃 -- 二级跳if Input.is_action_just_pressed("ui_accept") and is_jumping:velocity.y = -jump_forcechange_state_to(states.IN_AIR)is_jumping = falsevelocity = move_and_slide(velocity,Vector2.UP)pass# 更改状态func change_state_to(new_state:int):_state = new_state# 根据不同的状态,进行一些额外配置match _state:states.IN_AIR:passstates.ON_FLOOR:pass
代码分析
我们声明一个枚举states,然后书写两个表示在地面和空中的常量ONFLOOR和IN_AIR。然后声明_state变量存储当前玩家所处的状态,然后在_physics_process()中用match语句去检测_state变量当前的值,也就是玩家当前的状态,然后再根据具体的状态去进行相应的每帧处理。
可以看到,貌似我们这样写,代码比原来还多了,而且有相当一部分的代码重复,这是因为之前的写法共用了两个状态都能用的代码,而现在为了进行不同状态代码之间的完全区分,或者叫状态隔离。我们必须牺牲一些复用性。
这样写的好处是,当我们继续实现诸如爬墙、爬梯子或其他什么功能和状态时,我们可以继续简单的扩充我们的状态枚举states,然后轻松的用match语句的一个分支来进行这些状态下的处理。而不是再搞一堆is打头的变量,并嵌套各种if语句。
这是一个很好的进步,你可以用这种方式重构你的玩家或NPC的代码,精确而又无痛的处理它们的状态和行为。而这种状态隔离处理的思维,也是有限状态机的基础。
另外,我们还将给state变量赋值,封装为一个函数change_state_to()。这样一方面可以呈现更明显的语义,另一方面在其中我们利用另一个match语句,可以在状态切换后的瞬间对各个状态进行一定的统一配置。
当然我们并没有完全消灭is打头的变量,我们保留了is_jumping变量 ,通过和IN_AIR状态搭配,可以用来区分玩家是第一次跳还是第二次跳,从而实现二级跳的效果。
运行效果
运行效果基本上与上一节的没有区别。此处就不再展示。




