状态鸡就是 处理对象状态的机器。目的还是简化大型程序,分而治之。避免“史山代码”。
管理机(StateMachine)
顾名思义,即进行状态的管理。其实这里的代码就只是把初始状态传入(_ready中),一些对象(管理机自己)传入。
然后将各事件传给各状态(handle_input/update/physics_update)。定义一个切换状态的函数(transition_to)
看起来没进行啥真正的管理。各个状态都在它之下进行切换。新添加其它状态也一样。
extends Node
class_name StateMachine
signal transitioned(state_name) # 状态改变信号
@export var initial_state := NodePath() # 初始状态场景
@onready var state :State = get_node(initial_state) # 获取初始状态场景节点
func _ready() -> void:
await owner.ready # 等待父节点加载完成
for child in get_children(): # 获取所有子节点
if child is State: # 如果节点是状态节点
child.state_machine = self # 子节点状态机对象设为自己(把自己传入,便于每个状态调用)
state.enter() # 进入初始状态
func _unhandled_input(event: InputEvent) -> void: # 把输入事件传给状态机
state.handle_input(event)
func _process(delta: float) -> void: # 把更新事件传给状态机
state.update(delta)
func _physics_process(delta: float) -> void: # 把物理更新事件传给状态机
state.physics_update(delta)
# 切换状态
# 这里在状态中调用,切换为另一状态
func transition_to(traget_state_name: String):
if not has_node(traget_state_name):
return
state.exit() # 退出原状态
state = get_node(traget_state_name) # 根据traget_state_name名,获取节点
state.enter() # 进入新状态
emit_signal("transitioned",state.name) # 触发状态修改信号
状态类(State)
这是一个类,定义了常用函数(enter\exit…),获取状态机和玩家player节点等,用于控制。
具体每个状态需继承于此类。
extends Node
class_name State
var state_machine :StateMachine = null
var player
func _ready() -> void:
await owner.ready
player = owner.find_child("Player")
func handle_input(_event:InputEvent) -> void:
pass
func update(_delta:float) -> void:
pass
func physics_update(_delta: float) -> void:
pass
func enter() -> void:
pass
func exit() -> void:
pass
具体状态
例如Idle状态 state_idel.gd
extends State
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") # 获取项目中设置的重力
func enter():
# name为当前节点名(状态)
# owner 该节点的所有者
# state_machine 为状态机节点
# player 继承自State,获取玩家节点。这里与owner相同,只是在State类里自动加载为player
# print("进入",name, " ",owner, " ", state_machine," ",player)
player.velocity = Vector2.ZERO # 当前速度向量,单位为像素每秒
# 这里的play节点是定义在玩家Player节点之下的动画播放节点
# 代码播放动画节点下面的定义的动画
player.find_child("play").play(name.to_lower())
# 处理按键切换其它状态
func update(_delta) -> void:
if not player.is_on_floor():
player.velocity.y += gravity * _delta # gravity 重力强度
player.move_and_slide()
var direction := Input.get_axis("ui_left","ui_right") # 当左右移动时,进入Run状态
if direction != 0:
state_machine.transition_to("Run")
if Input.is_action_just_pressed("ui_accept"): # 跳
state_machine.transition_to("Jump")
player.velocity.y = player.JUMP_VELOCITY
if Input.is_action_just_pressed("ui_k"): # 攻击
state_machine.transition_to("Attack")
func exit():
print("退出",name)