《Unity3D高级编程之进阶主程》第五章,3D模型与动画(三) - 状态机

如何用状态机模拟人物行为动作。

什么是状态机?

状态机有两种,一种是有限状态机,一种是无限状态机。有限状态机运用的地方比较多,而无限状态机我还没看见过有在游戏项目中运用的。我们这里讲的状态机都是有限状态机。

有限状态机简单描述下,实例本身有很多种状态,实例从一种状态切换到另一种状态的动作就是状态机转换,而转换是有条件的,这个条件就是状态机之间的连线。

打个比方,人有三个状态健康,感冒,康复中。触发的条件有淋雨(t1),吃药(t2),打针(t3),休息(t4)。状态机的连接图可以是这样,健康-(t4休息)->健康;健康-(t1淋雨)->感冒;感冒-(t3打针)->健康;感冒-(t2吃药)->康复中;康复中-(t4休息)->健康,等等。状态在不同的条件下跳转到自己或不同状态中去。

===

状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:

①现态:是指当前所处的状态。

②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

游戏中人物行为动作中使用状态机

游戏中状态机的关键是事件和控制状态的控制类。状态机的数量和作用都会因系统的不同而不同,触发条件也各异的,唯有事件机制和控制类是状态机的不变的功能。

事件机制在进入状态和退出状态时,触发进入事件和退出事件,这个是状态启动运作和停止运作的起点。

状态在满足转换条件后,退出状态前向当前状态发起退出事件,告诉当前状态机,你将停止运行,停止运行前需要处理什么逻辑请赶快做,然后再向新状态发起进入事件,告诉新状态你将要开始运作,运作前的有什么逻辑或者准备工作请尽快做。这样每次状态的切换都能合理的告诉当前状态和将要切换的状态进行事件的调用。

状态机在游戏中哪些地方会使用到?

所有能够构成独立状态的系统或者功能都能使用状态机来表现:

    1,    场景切换。

    场景是独立的,只能有一个场景展示在游戏中,所以场景的切换可以用状态机来表示。比如当前是登录场景,点击登录后切换到游戏场景,这时需要把登录场景的UI销毁,UI的销毁工作是登录场景状态在退出时触发的退出事件中做的事。

    之后进入到游戏场景状态时,要先把游戏场景的UI创建出来,这是游戏场景状态在触发启动事件时要做的事。

    2,    人物行为状态切换。

    人物只能做一个动作状态,比如攻击动作状态,比如防守动作状态,比如死亡状态,又比如人物跑步状态,这些行为都只能用单独的一个状态来表示,但也有人物边跑步边吃东西的时候,这时我们也会有几种方式去构建,比如我们把人物跑步且吃东西另外创建一个状态来运行,也可以把跑步状态里加一个吃东西的参数,让跑步这个状态机来运行不一样的跑步动作。

    3,    宝箱,机关等具有多动画的元素都可以构成独立的状态。

    每个动画的播放都可以成为一个状态,比如打开状态下的宝箱,比如触发机关时的状态等等。

    4,    AI。

    用状态机来封装AI是最常见到的,每个AI状态都是独立运行的。比如激怒状态,此状态中会不断向周围的敌人发起攻击,如果20秒后恢复到平静状态不再攻击,又比如巡逻状态,在某个点周围或者按一定路线进行走动,如果5米内发现敌人,就进去激怒状态等等。

我们先按常见的需求把人物行为动作划分一下:

    1,    休息状态。原地不动,并且重复做一个休息的动画。

    2,    攻击状态。播放攻击动画,并且对目标或前方进行攻击。

    3,    技能状态。技能稍微复杂点,因为每个技能都不一样,所以技能状态里面的逻辑可以由不同的技能类来实现。比如建个技能基类class SkillBase,里面有几个统一的接口,然后子类对基类进行继承后,细化技能的细节。

    4,    防御状态。播放防御动画,当受到攻击时,不切换受伤状态。

    5,    受伤状态。播放受伤动画,完毕后自动进入休息状态。

    6,    行走与跑步状态。播放行走或跑步动画,并根据操作输入移动方向。

    7,    跳跃状态。播放跳跃动画,并上下移动人物进行跳跃,下落时底部受到碰撞就进入休息状态。

    8,    死亡状态。播放死亡动画,并且不再受到任何指令而转入任何状态。

这里对每个状态编写一些伪代码来描述状态机在人物行为中的运作,如下:

Class BaseState
{
    Public virtual void OnEnter(){}
    Public virtual void OnExit(){}
    Public virtual void Update(){}
}

Class IdleState
{
    Public override void OnEnter()
    {
        role.playAnimation(idle,loop);
    }
}

Class HurtState
{
    Public override void OnEnter()
    {
        role.playAnimation(hurt,Once);
    }
    
    Public override void Update()
    {
        If(!Role.IsPlayingAnimation(hurt))
        {
            HurtFinish();
        }
    }

    Public override void OnExit()
    {
        GotoIdleState();
    }

}

Class AttackState
{
    Public override void OnEnter()
    {
        Role.playAnimation(attack,once);
    }

    Public override void Update()
    {
        If(!Role.IsPlayingAnimation(attack))
        {
            AttackStateFinish();
        }
    }

    Public override void OnExit()
    {
        GotoIdleState();
    }
}

Class MoveState
{
    Public override void OnEnter()
    {
        Role.playAnimation(walk,loop);
    }

    Public override void Update()
    {
        Move();
    }

    Public override void OnExit()
    {
        GotoIdleState();
    }
}

上述代码表达了对状态的描述,其中空闲状态,在没有收到任何指令时只循环播放Idle动画其他什么都不干;攻击状态,进入时播放攻击动画,并在动画结束后返回Idle状态并等待指令;移动状态在进入时播放walk动画并移动,在退出时进入Idle状态。

除了事件对状态起了关键的作用外,控制状态的控制类也是关键,它就像是状态的管理类,用于管理状态的入口和出口。

以下是伪代码:

Class RoleStateController
{
    Private IdleState idleState;
    Private MoveState moveState;
    Private AttackState attackState;
    Private HurtState hurtState;

    Private BaseState currentState;
    
    Public void OnHurt()
    {
        ReduceHP();
        If(currentState != hurtState)
        {
            ChangeToHurtState();
        }
    }

    Public void InputAttack()
    {
        If(currentState  == hurtState) return;  //受伤状态下不可攻击
        If(currentState == attackState) return; //攻击状态还没结束时不可重新开始攻击
        ChangeToAttackState();
    }

    Public void InputMove(){}
}

在状态机控制类中,存储了各个状态,并且提供了输入的接口,这种输入的事件是由状态机外部提供的,比如发起攻击,比如受到伤害,比如向前向后移动等等,在外部需要状态机触发状态改变时,发起了对状态机的输入事件。

这样就是状态机控制类需要做的事,简单来说就是存储状态,并提供输入事件接口。

有了状态机的控制类就有了状态机的管理,就可以对各种逻辑的控制,比如硬值,受伤中不可行走和攻击,攻击中不可移动等,这些当状态在切换时的条件限制,是阻挡还是通过,该切换什么状态,都是由控制类来决定的,它就是状态机系统的大脑。

感谢您的耐心阅读

Thanks for your reading

  • 版权申明

    本文为博主原创文章,未经允许不得转载:

    《Unity3D高级编程之进阶主程》第五章,3D模型与动画(三) - 状态机

    Copyright attention

    Please don't reprint without authorize.

  • 微信公众号