Unity 有限状态机(Finite State Machine)的理解 与 实现简单的可插拔(Pluggable)AI脚本对象。

本文介绍如何在Unity中利用有限状态机实现可插拔的AI脚本对象。通过具体的坦克游戏实例,详细展示了状态机的基本概念、核心组件及其工作流程。文章提供了状态、动作、决定、状态转换等关键类的具体实现。

#Unity 有限状态机(Finite State Machine)的理解 与 实现简单的可插拔AI脚本对象。

参考官方教程:Pluggable AI With Scriptable Objects
Github完整项目:TanksPluggableAI
大神Blog:【游戏设计模式】之三 状态模式、有限状态机 & Unity版本实现

  一般的游戏AI都是使用状态机的设计模式来实现的。发现官方有教程,就跟了一遍,这里就总结一下。
  先简单说一下状态模式。就是根据当前状态和对应条件,选则需要转换的下一个状态。例如本文的例子,坦克默认状态是巡逻(状态),在发现有敌人时(条件),转换成追杀(状态),在敌人逃脱或死掉了之后(条件),又变成巡逻(状态)。

  游戏场景:绿色标签为巡逻点。蓝色标签为玩家出生点。红色为AI出生点。(如图玩家被两个AI追杀。)


AI对象结构

  每个AI对象都有一个状态控制器(StateController)脚本组件,包含一个当前状态(State),且状态包含需要执行动作(Action),还有状态转换的条件(Transition)。

  Unity中文件分类层级如下。其中DefaultEnemyStats是默认AI的一些配置。包括攻击距离,移动速度,旋转速度等等。


State (状态)

using UnityEngine;

[CreateAssetMenu (menuName = "PluggableAI/State")]
public class State : ScriptableObject 
{
    public Action[] actions;                        //动作
    public Transition[] transitions;                //通过决定,选择下一种动作决定
    public Color sceneGizmoColor = Color.gray;      //拿来渲染eyes的Gizmos颜色

    //每一帧更新状态,在StateController的OnUpdate中调用。
    public void UpdateState(StateController controller)
    {
        DoActions(controller);                      //执行动作
        CheckTransition(controller);                //检测转换状态
    }

    //顺序执行动作列表的动作。
    private void DoActions(StateController controller)
    {
        for (int i = 0; i < actions.Length; i++)
            actions[i].Act(controller);
    }

    //检查所有转换状态,并改变状态。
    private void CheckTransition(StateController controller)
    {
        for (int i = 0; i < transitions.Length; i++)
        {
            //这里条件转换只有两个,所以直接用Bool类型来判断。当然也可以有多种条件转换。
            bool decisionSucceeded = transitions[i].decision.Decide(controller);

            if (decisionSucceeded)
                controller.TransitionToState(transitions[i].trueState);
            else
                controller.TransitionToState(transitions[i].falseState);
        }
    }

}
状态名说明配置
Remain State保持原来状态。不包含任何属性。略。
PatrolChaser巡逻者状态。在巡逻点之间巡逻,如果检测到敌人,转为追杀状态。
ChaseChaser追杀者状态。导航到目标一定距离并持续攻击,直到目标死掉,转回巡逻者模式。
PatrolScanner同Patrol Chaser。巡逻到敌人,转为追杀。
ChaseChaser同Chase Chaser 。追杀敌人,但到目标死掉,转换成警觉模式。
AlertScanner警觉模式。其实就是旋转扫描敌人,直到扫描到目标或超过了一定时间。

  通过上表其实可以发现有两个非常相似的动作,只是为了演示Alert Scanner 的。其实是可以优化的,就是修改Transition的结构,不再是只有两种状态选择,而是多种,这样就可以更好的重用状态。


Action(动作)

using UnityEngine;

public abstract class Action : ScriptableObject 
{
    //State直接调用这个方法来执行动作。
    public abstract void Act(StateController controller);
}
动作名说明原理
PatrolAction巡逻动作。从StateController搞来巡逻点列表。在这些巡逻点之间巡逻移动。设置巡逻点为导航目标点,到达后设置下一个巡逻点为导航点。
ChaseAction追随动作。最短距离到达目标。设置目标为导航目标点,导航过去就是了。
AttackAction攻击动作。攻击目标。射出检测射线(红色的),检测到目标就发炮攻击。

Decision(决定)

using UnityEngine;

public abstract class Decision : ScriptableObject 
{
    //通过这个方法的返回值来判断决定的选择。
    public abstract bool Decide(StateController controller);
}
决定名说明原理
LookDecision检测决定。检测到目标就设置为追踪目标,并且返回True。射出检测球体射线(绿色的)来检测。
ActiveStateDecision活动状态决定。就是判断追踪的目标是否active(激活状态)。直接返回追踪目标的active的bool值。
ScanDecision扫描决定。说是扫描其实并没有扫描,只是在转圈而已。一直转到外部条件改变状态,或者超过了扫描限定的时间。停止导航。配合Time.deltaTime旋转自己。返回是否超过扫描限定时间。

Transitions(状态转换)

//状态转换。通过决定的返回值,选则两种状态中其中一个
[System.Serializable]
public class Transition 
{
    public Decision decision;
    public State trueState;
    public State falseState;
}

测试结果


第一种,巡逻->发现目标->追杀->目标死亡->巡逻。(颜色配合下图样例使用。)

这里写图片描述

1. 起始 Patrol Chase 状态,巡逻点之间移动,还没有检测到目标,眼里射出的是清澈的绿色。

2. 发现了目标,转为Chase Chaser 状态,射出了充满愤怒的红色射线,并且一直攻击目标。

3. 当目标化为灰烬,转回 Patrol Chase 状态。


第二种,巡逻->发现目标->追杀->目标死亡->旋转扫描目标->{两种情况:1. 发现目标,回到第二步。2. 超过扫描时间,回到第一步(巡逻)。(颜色配合下图样例使用。)

这里写图片描述

1. 起始 Patrol Scanner 状态,巡逻,眼里射出的是原谅的绿色。

2. 发现刚刚杀了蓝孩子的小红,转为 Chase Scanner 状态,追杀之。

3. 杀掉小红之后,转为 Alert Scanner 状态。旋转扫描周围是否有敌人。

4. 没有扫到,回到 Patrol Scanner 状态。


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值