实体状态机设计(1)

实体状态机设计(1)

使用节点式的方式来设计实体状态机

脚本部分分为

  • 状态脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    状态类是所有状态的基类,提供基础的方法和切换信号
    # state.gd
    class_name State extends Node

    # 切换状态的信号
    signal switch_state(state: State)

    func enter_state() -> void:
    pass

    func exit_state() -> void:
    pass

    func update(_delta: float) -> void:
    pass

    func physics_update(_delta: float) -> void:
    pass

  • 状态机脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    状态机是管理状态的具体部分
    包含初始化状态、记录当前状态、更新状态、更新物理状态
    # state_machine.gd
    class_name StateMachine extends Node
    # 导出变量来存储初始化状态
    @export var initial_state: State

    # 当前状态的引用
    var active_state: State:
    set(new_value):
    active_state = new_value
    print("Changed to ",active_state.name)

    # 在ready函数中遍历所有状态
    # 把switch_state 与 change_state函数链接,由状态机来处理
    func _ready() -> void:
    for child_state: State in get_children():
    child_state.switch_state.connect(change_state)

    change_state(initial_state)

    func _process(delta: float) -> void:
    # 如果激活状态,更新状态
    if active_state:
    active_state.update(delta)

    func _physics_process(delta: float) -> void:
    if active_state:
    active_state.physics_update(delta)

    func change_state(new_state: State) -> void:
    if new_state == active_state:
    return

    # 退出当前状态
    if active_state:
    active_state.exit_state()

    # 配置为新状态
    active_state = new_state

    # 进入新状态
    if active_state:
    active_state.enter_state()

  • 动作脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
以站立和行走状态为例,具体状态脚本有进入状态、触发更新状态信号两个部分
# Idle_state.gd
extends State

@export var walk_state: State

func enter_state() -> void:
pass # play animation

func update(_delta: float) -> void:
if Input.get_vector(
"ui_left", "ui_right", "ui_down", "ui_up") != Vector2.ZERO:
switch_state.emit(walk_state)

# walk_state.gd
extends State

@export var idle_state: State

func enter_state() -> void:
pass # play animation

func update(_delta: float) -> void:
if Input.get_vector(
"ui_left", "ui_right", "ui_down", "ui_up") == Vector2.ZERO:
switch_state.emit(idle_state)

流程一:静止(Idle)切换到行走(Walk)

假设当前角色处于 Idle 状态,玩家按下了方向键。

步骤 执行位置 具体动作与代码逻辑
1. 输入检测 Idle_state.gd 引擎调用 active_state.update(delta),实际执行 Idle_stateupdate 方法。代码检测到输入向量 不等于 Vector2.ZERO
2. 发起切换 Idle_state.gd 执行 switch_state.emit(walk_state)注意:此时 Idle 并不知道状态机是谁,它只是把信号抛出去。
3. 信号拦截 StateMachine.gd 由于提前绑定了信号,状态机立刻捕获到这个信号,并调用 change_state(walk_state)
4. 退出旧状态 StateMachine.gd 判断新旧状态不同,执行 active_state.exit_state(),即调用 Idle_stateexit_state(比如停止待机动画)。
5. 更新引用 StateMachine.gd 执行 active_state = new_state,此时激活状态变为 Walk,控制台打印 "Changed to Walk"
6. 进入新状态 StateMachine.gd 执行 active_state.enter_state(),即调用 Walk_stateenter_state(比如播放行走动画)。
7. 下一帧生效 - 从下一帧开始,_process_physics_process 透传的对象变成了 Walk_state,角色的移动逻辑正式开始。

流程二:行走(Walk)切换回静止(Idle)

假设当前角色处于 Walk 状态,玩家松开了所有方向键。

步骤 执行位置 具体动作与代码逻辑
1. 输入检测 Walk_state.gd 引擎调用 active_state.update(delta),执行 Walk_stateupdate。代码检测到输入向量 等于 Vector2.ZERO
2. 发起切换 Walk_state.gd 执行 switch_state.emit(idle_state),请求切换回静止状态。
3. 信号拦截 StateMachine.gd 状态机再次捕获信号,调用 change_state(idle_state)
4. 退出旧状态 StateMachine.gd 执行 active_state.exit_state(),调用 Walk_stateexit_state(停止走路粒子特效或脚步声)。
5. 更新引用 StateMachine.gd active_state = idle_state,控制台打印 "Changed to Idle"
6. 进入新状态 StateMachine.gd 执行 active_state.enter_state(),调用 Idle_stateenter_state(播放待机动画,重置呼吸参数等)。
7. 下一帧生效 - 下一帧起,_process 执行 Idle 的逻辑,角色停止移动。

未来会在这个状态机基底上,进行角色运动的设计