【Unity】有限状态机在动画系统中的运用

一、何为“有限状态机”

1.FSM的名词解释

“有限状态机”(Finite State Machine,简称FSM),是一个较为宽泛的抽象系统概念,顾名思义,“有限”是指状态机中的各种状态都是已经提前预设好的,不存在跳脱出该系统预定状态以外的其他情况;而“状态机”则是对各个预设状态的“执行”以及“状态切换”。
 

2.FSM的简单理解

简单讲,有限状态机其实就相当于一张离散数学图论中的“强连通图”(图中的任一顶点的出度和入度都不为0),即从任一顶点(状态)出发,都能通过有向路径(判定条件)到达图中的任意其他顶点(状态),相对的,任意其他顶点(状态)也能通过有向路径到达该顶点(状态)。

强连通图例图

图1: 强连通图示例图

unity中一个角色的常见运动系统

图2: Unity中一个角色的常见运动系统

总得来说,FSM是帮助将复杂的状态和行为分解为离散的单元模块,从而更好地理解与管理系统的行为。

二、FSM在unity动画系统的使用

1.为什么选择使用FSM来管理动画系统

相信不少unity小白,在刚接触Animator系统时,肯定多多少少都编过“蜘蛛网”

地狱绘图(编过蜘蛛网的都知道这份痛苦)

但笔者在此不是说完全否定了Animator动画连连看不行,而是不应该将大量的精力,或者说逻辑放在连连看上,首先这本身就超出了动画系统作为“view”层的负责权限,其次,维护起来也是相当的麻烦。

而FSM很好的解决了这个问题,将逻辑分放在各个状态中,让Animator专心负责“播放对应动画”的职责。
 

2.使用FSM的好处

  1. 化繁为简:单元化动画系统,将复杂的动作拆解成一个个独立单元,能专心处理当前正在编写状态的逻辑。

  2. 容易维护:能以较小的代价随时增删动作状态,假如项目在开发过程中突然需要增加/删掉跳跃动作,那么只需要进入到各个已经定义好的状态中将有关于“跳跃状态”的切换逻辑增加/删掉即可。

  3. 对合作项目友好:不论是新进人员还是临时外援,FSM系统都很好的约束了其他人对于项目动画系统的改动,如果需要增加/删除动作状态,那必须严格遵守项目已经定义好的FSM框架,这对于项目管理而言是高效率的。

 

3.简单FSM的实现

状态基类BaseState定义

namespace FSM
{
    public abstract class BaseState
    {
        public StateEnum state;
        protected BaseStateMachine stateMachine;
        public BaseState(BaseStateMachine sm, StateEnum state_)
        {
            this.state = state_;
            this.stateMachine = sm;
        }
        public virtual void Enter() { }

        public virtual void UpdateLogic() { }
        public virtual void FixUpdateLogic() { }
        public virtual void UpdatePhysics() { }

        public virtual void UpdateAnimatorMove() { }

        public virtual void Exit() { }

    }
}

 

状态机基类BaseStateMachine定义

using UnityEngine;
using System;

namespace FSM
{
    public abstract class BaseStateMachine : MonoBehaviour
    {
        private BaseState currentState = null;

        private void Start()
        {
            currentState = GetInitialState();
            if (currentState != null)
                currentState.Enter();

        }

        private void Update()
        {
            if (currentState != null)
                currentState.UpdateLogic();
        }

        private void FixedUpdate()
        {
            if (currentState != null)
                currentState.FixUpdateLogic();
        }

        private void LateUpdate()
        {
            if (currentState != null)
                currentState.UpdatePhysics();
        }
        private void OnAnimatorMove()
        {
            if (currentState != null)
                currentState.UpdateAnimatorMove();
        }

        public void ChangeState(BaseState targetState,bool exit = true)
        {
            if (currentState != targetState)
            {
                if (currentState != null && exit)
                    currentState.Exit();

                currentState = targetState;

                if (currentState != null)
                    currentState.Enter();
            }
        }

        protected virtual BaseState GetInitialState()
        {
            return null;
        }
    }
}

4.简单FSM的使用技巧

①关于Unity声明周期函数的继承与执行关系

继承于BaseStateMachine的类并不需要再次定义Start,Update,OnAnimatorMove等Unity生命周期函数,

因为当一个类的继承链上存在mono类,则在父级定义过的生命周期函数是会自动调用的,

若再次定义,则执行的是子类中定义的生命周期函数,

故继承于BaseStateMachine类的实例化状态机脚本中是不能再次定义Start,Update等生命周期函数的。
 

②状态分层

为了避免逻辑重复而导致的代码冗余,在进行状态定义时应该事先对状态进行分析,判断是否存在其他状态与将要定义的状态存在逻辑重复,如果有重复则应该提取出来作为这些有相同逻辑状态的“基状态”。

例如idle,walk,run,sprint等状态均为在地表上,Y轴并没有移动,则应该抽象出“Grounded”基状态;

又例如jump,falling等应该抽象出“Air”基状态。
 

③同级状态之间的切换是否执行Exit()

在满足切换条件后执行ChangeState(BaseState targetState,bool exit = true)方法时,根据你定义的状态层级关系,判断是否真的需要执行exit方法。

 

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇