Unity之有限状态机

这几天我在试图学习Unity里的动作系统,了解到“有限状态机”这个东西,之前一直有听说过,以前总感觉很高大上,花了一节水课的时间大概弄懂了,于是分享一下。

温馨提示:在开始阅读本文之前,请确保你了解C#语言中接口、继承、抽象类等相关知识点,否则可能会看不懂)。

什么是有限状态机?

对于玩家和怪物,总会存在几个状态:比如待机、跑步、攻击、巡逻...这些都可以称之为状态。我们在Unity开发中最常接触的有限状态机就是我们的Animator窗口——它就是一个有限状态机。 使用有限状态机,可以更方便我们去进行人物行为动作管理和逻辑管理,比如玩家处于待机状态,按下W键切换到跑步状态、跳跃时进入跳跃状态,我们把不同状态用状态机进行管理,就会好很多。

通用的有限状态机框架

在我观看数位、海内海外的博主的视频,发现他们都会采用同一套框架,逻辑、代码都是极其相似的,于是进行总结。
首先我们新建一个名为IState.cs:
1
2
3
4
5
6
7
8
using UnityEngine;
public interface IState
{
public void Enter();
public void Exit();
public void Update();
}

作用是实现一个接口,相当于一套代码规范,我们的所有状态:待机、跑步、跳跃等都要实现接口里的方法。


第二步,我们写状态继承该接口,这里我新建个脚本IdleState.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using UnityEngine;
public class IdleState
{
//初始化状态机
private FSM manager;

public IdleState(FSM manager)
{
this.manager = manager;
}

public void Enter()
{
//书写逻辑
}
public void Exit()
{
//书写逻辑
}
public void Update()
{
//书写逻辑
}
}

第三步,我们开始写状态机主体框架,新建FSM.cs文件:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Windows.Speech;

public enum StateType
{
idle,
run,
jump
}

public class FSM : MonoBehaviour
{
//当前状态
private IState currentIState;
//字典存储状态
public Dictionary<StateType, IState> states;
public FSM()
{
//设置默认状态
this.states = new Dictionary<StateType, IState>();
}

//添加状态
public void AddState(StateType state, IState istate)
{
if (states.ContainsKey(state))
{
Debug.Log("请勿重复添加");
}
states.Add(state, istate);
}
//转换状态
public void TransformState(StateType state)
{
if (states.ContainsKey(state))
{
Debug.Log("已经转换到此状态了!");
}
if (currentIState != null)
{
currentIState.Exit();
}
currentIState = states[state];
currentIState.Enter();
}

void Start()
{
//注册状态
states.Add(StateType.idle,new IdleState(this))
}

void Update()
{
//状态每帧执行的函数
currentIState.Update();
}
}

让我们重新理清逻辑:
IState.cs:所有状态都有“进入状态”“状态执行中”“退出状态”三个方法,于是我们用代码写个接口,所有状态均继承这个接口。
FSM.cs:状态机的核心部分,首先用枚举定义我们有几种状态,接着我们用IState的一个实例currentState,根据“父装子”原则,用于代表我们当前的状态。
同时,我们定义一个字典states,用于存储我们的状态,接下来构造函数。然后我们写添加状态和转换状态的方法,在Start函数里注册完我们的所有方法,Update函数里去进行状态调用。


那么状态机用来干什么?主要是提升我们的开发效率、简化代码吧,它可以用来制作敌人AI逻辑、也可以用来丰富我们的角色控制器,用处还是很大的。