Godot 3.4.2
下面是一个简单的有限状态机基类,之后是一个使用状态机的思路
State.gd
#============================================================
# State
#============================================================
# 子状态
#============================================================
# @datetime: 2022-3-15 17:03:14
#============================================================
class_name State
extends Node
onready var state_machine = get_parent()
#============================================================
# Set/Get
#============================================================
func get_state_machine():
return state_machine
func get_current_state() -> State:
return state_machine.get_current_state() as State
func get_blackboard():
return get_state_machine().get_blackboard() as StateBlackboard
#============================================================
# 自定义
#============================================================
## 进入到这个状态时
func enter(data):
pass
## 退出这个状态时
func exit():
pass
## 执行到当前状态时,会切换为调用这个方法
func state_process(delta):
pass
## 切换状态
func switch_to(state, data: Dictionary = {}):
state_machine.switch_to(state, data)
StateMachine.gd
#============================================================
# State Machine
#============================================================
# 状态机
#============================================================
# @datetime: 2022-3-15 17:02:55
#============================================================
class_name StateMachine
extends Node
signal state_changed(last_state, current_state)
var current_state
var current_state_node : State
var blackboard : StateBlackboard setget , get_blackboard
var states := {}
#============================================================
# Set/Get
#============================================================
func get_state_node_list() -> Array:
return states.values()
func get_blackboard():
if blackboard == null:
for child in get_children():
if child is StateBlackboard:
blackboard = child
return blackboard
func get_state_node(state) -> State:
return states[state] as State
#============================================================
# 内置
#============================================================
func _ready():
# 找到所有子状态机
for child in get_children():
if child is State:
# 根据节点的 name 作为 key 添加状态
states[child.name] = child
#child.set_physics_process(false)
#child.set_process(false)
if states.size() > 0:
# 第一个节点
current_state = states.keys()[0]
current_state_node = get_state_node(current_state)
current_state_node.enter(null)
else:
set_physics_process(false)
printerr("没有添加状态节点,状态机没有启动")
func _physics_process(delta):
current_state_node.state_process(delta)
#============================================================
# 自定义
#============================================================
func switch_to(state, data: Dictionary = {}):
if state != current_state:
current_state_node.exit()
emit_signal("state_changed", current_state, state)
# 切换到当前
current_state = state
current_state_node = get_state_node(current_state)
current_state_node.enter(data)
Blackboard.gd(存储状态机的全局数据)
#============================================================
# Blackboard
#============================================================
# 状态黑板
#============================================================
# @datetime: 2022-3-15 17:31:58
#============================================================
class_name StateBlackboard
extends Node
var data : Dictionary = {}
func _enter_tree():
# 状态机根节点的 blackboard 属性为自身
get_parent().blackboard = self
在做的时候,我会创建对应类型的状态机、状态和对应的黑板
比如我要给 Player 节点设计状态机,我会先创建对应的 (Player状态机)、(Player状态基类)、(Player状态机黑板)
这个设计之后,我们开始添加如下节点结构
PlayerStateMachine
PlayerBlackboard
多个 PlayerState

如上图所示,、、 等都是继承自 ,这些状态在某些功能需求上是相同的,比如在 、 、、 都可以接收输入进行移动,在 里我们需要对这些功能进行编写。(注意这些都是共用的功能,如果是共用一个数据,一般都写在 里)。
比如下面几个功能
Idle 状态里

Move 状态

Jump 状态

PlayerState 中,Player 可以执行的能力。

input 开头的方法是指可以执行的功能, 接收 移动 攀爬 跳跃 落下 等等行为,每个行为都会返回一个 布尔值 ,用以判断是否执行了这个功能。
如果功能是可以执行多个的,比如 可以在移动的时候执行其他功能,则直接不在 if 里进行优先级判断,而在 if 中的,则会有一个优先级,只能执行其中的一个动作,不能同时执行,因为总不能说,在跳跃的时候又同时在爬楼梯。
这样,我们的一个状态机就完成了。
