游戏 AI 设计之 FSM 有限状态机

FSM 有限状态机

一、概述

  有限状态机finite-state machine,缩写:FSM)又称有限状态自动机finite-state automaton,缩写:FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移动作等行为的数学计算模型
  从历史上来说,有限状态机是一个被数学家用来解决问题的严格形式化的设备。在人工智能变成中,我们可以建 FSM 理解为:
  一个有限状态机是一个设备,或是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得从一个状态变换到另一个状态,或者是促使一个输出或者一种行为的发生。一个有限状态机在任何瞬间只能处在一种状态

二、分析

  有限状态机,一般称作 FSM。常作为游戏 AI 设计的首选,尽管更专业的智能体结构越来越普及,但 FSM架构依然流行的原因如下

  • 编程快速简单
    有很多方法编码一个有限状态机,并且几乎乎所有的有限状态机实现都相当的简单。
  • 易于调试
    因为一个游戏智能体的行为被分解成简单的易于管理的块,如果一个智能体开始变得行动怪异,会通过对每一个状态增加跟踪代码来调试它。用这种方法,人工智能程序员可以很容易跟踪错误行为出现前的事件序列,并且采取相应的行动。
  • 计算开销小
    有限状态机几乎不占用珍贵的处理器时间,因为它们本质上遵守硬件编码的规则。除了 if-this-then-that 类型的思考处理之外,是不存在真正的“思考”的。
  • 直觉性
    人们总是自然地把事物思考为处在一种或另一种状态。并且我们也常常提到我们自己处在这样那样的状态中。有多少次你“使自己进入一种状态”或者发现自己处于“头脑的正确状态”。当然人类并不是像有限状态机一样工作,但是有时候我们发现在这种方式下考虑我们的行为是有用的,比如你处于口渴状态,你就会想去喝水。相似地,将一个游戏智能体的行为分解成一系列状态并且创建需要的规则来操作它们是相当容易的。出于同样的原因,有限状态机能够使你很容易地与非程序员(例如与游戏制作人和关卡设计师)来讨论你的人工智能的设计,能够更好地进行设计概念的沟通和交流。
  • 灵活性
    一个游戏智能体的有限状态机可以很容易地由程序员进行调整,来达到游戏设计者所要求的行为。同样通过增添新的状态和规则也很容易扩展智能体的行为的范围。此外,当你的人工智能技术提高了,你会发现有限状态机提供了一个坚固的支柱,使你可以用它来组合其他的技术,例如模糊逻辑和神经网络。

三、代码实现

下面我们实现一个简易的游戏状态机

  • Hero 在没有攻击目标的时候是 Idle(待机) 状态
  • 在发现怪物时会切换到 Attack(攻击) 状态
  • 没有攻击目标 切回 Idle 状态
  • 发现 Boss 或 自身血量低于 10 的时候 切换到 Escape (逃跑)状态
    状态装换示意图
状态转换示意图

1、StateMachine

_curState:当前状态,用于表示当前 Entity 所处的状态
_preState:保存 Entity 的上一个状态,方便回溯
_globalState :这是一个特殊的状态,可与 _curState 共存,引进全局状态的原因是为了解决一种优先级高的状态,可以保证在任意时刻切换到指定状态。比如模拟人生的游戏里面,有个人正在工作,突然想要上厕所,那么需要打断当前的状态(使用 _preState 记录)并切换到上厕所状态。在上完厕所后,又继续恢复工作(恢复上一个状态)。

public class StateMachine
{
    private Entity _entity = null;
    private State _globalState = null; // 全局状态
    private State _preState = null; // 上一个状态

    private State _curState = null; // 当前状态
    public State CurState { get => _curState; }

    private Dictionary<StateType, State> _dicState = null;

    public void Init(Entity entity, List<string> listStateName)
    {
        _entity = entity;
        _dicState = new Dictionary<StateType, State>();

        if (null != listStateName) 
        {
            int len = listStateName.Count;
            for (int i = 0; i < len; i++) 
            {
                string className = "FSM.AI.States." + listStateName[i];
                Type stateType = Type.GetType(className);

                State state = Activator.CreateInstance(stateType) as State;
                state.Init(_entity);

                AddState(state);
            }
        }
    }

    public void AddState(State state)
    {
        StateType stateType = state.StateType;
        if (!_dicState.ContainsKey(stateType))
        {
            _dicState.Add(stateType, state);
        }
        else
        {
            Console.WriteLine(" State is already exist ! State : " + stateType);
        }
    }

    public void Update()
    {
        if (null != _curState)
        {
            _curState.Execute();
        }

        if (null != _globalState)
        {
            _globalState.Execute();
        }
    }

    public void ChangeState(StateType state, object data = null)
    {
        if (_dicState.ContainsKey(state))
        {
            _preState = _curState;
            _curState = _dicState[state];

            if (null != _preState)
            {
                _preState.Exit();
            }

            _curState.Enter(data);
        }
        else
        {
            Console.WriteLine(" State no exist ! " + _entity.GetEntityID() + " State " + state);
        }
    }


    public void OnMessage(string messageID, object data = null) { }

}

2、State

Enter():进入状态时调用
Execute():每次执行时调用
Exit():退出状态时调用

using FSM.Entities;

namespace FSM.AI.States
{
    public enum StateType
    {
        Idle = 0,   // 待机
        Attack = 1, // 攻击
        Escape = 2, // 逃跑
    }

    public abstract class State
    {
        protected Entity _entity = null;
        protected StateType _stateType = StateType.Idle;
        public StateType StateType { get => _stateType; }

        public virtual void Init(Entity entity, object data = null)
        {
            _entity = entity;
        }

        public virtual void Enter(object data = null) { }

        public abstract void Execute();

        public virtual void Exit() { }

        public virtual void OnMessage(string msgID, object data = null) { }

    }
}

3、Hero 相关状态

  • HeroIdleState 待机
  • HeroAttackState 攻击
  • HeroEscapeState 逃跑
using FSM.Entities;
using FSM.Manager;
using System.Collections.Generic;

namespace FSM.AI.States.Hero
{
    public class HeroIdleState : State
    {
        private const int ESC_HP = 10;

        public override void Execute()
        {
            Console.WriteLine($" {_entity.GetEntityType()} 待机中");

            // 发现 BOSS 或者 血量小于 10 就逃跑
            if (_entity.Data.hp < ESC_HP)
            {
                _entity.ChangeState(StateType.Escape);
            }

            List<Entity> listMonster = EntityManager.Instance.GetMonsters();
            if (null != listMonster)
            {
                if (listMonster.Exists(it => it.GetEntityType() == EntityType.Boss))
                {
                    _entity.ChangeState(StateType.Escape);
                }
                else
                {
                    Entity monster = listMonster.Find(it => it.GetEntityType() == EntityType.Monster);
                    if (null != monster && !monster.IsDeath())
                    {
                        _entity.ChangeState(StateType.Attack);
                    }
                }
            }
        }
    }
    
    public class HeroAttackState : State
    {
        public override void Init(Entity entity, object data = null)
        {
            base.Init(entity, data);
            _stateType = StateType.Attack;
        }

        public override void Execute()
        {
            List<Entity> listMonster = EntityManager.Instance.GetMonsters();
            if (null != listMonster)
            {
                Entity monster = listMonster.Find(it => it.GetEntityType() == EntityType.Monster);
                if (null != monster)
                {
                    _entity.Attack(monster);
                }
                else
                {
                    _entity.ChangeState(StateType.Idle);
                }
            }
            else
            {
                _entity.ChangeState(StateType.Idle);
            }
        }
    }
    
    public class HeroEscapeState : State
    {
        public override void Init(Entity entity, object data = null)
        {
            base.Init(entity, data);
            _stateType = StateType.Escape;
        }
    
        public override void Execute()
        {
            Console.WriteLine(" Hero:敌人太强了 ! 跑路 !");
        }
    }
}

4、Entity

GetEntityID():获取 Entity 的唯一 ID
ChangeState():切换 Entity 状态
OnUpdate():定时刷新 Entity 状态,在游戏中一般是每帧刷新,Demo 中使用 0.5s 刷新一次

using FSM.AI;
using FSM.AI.States;
using FSM.Data;
using System;

namespace FSM.Entities
{
    public enum EntityType
    {
        Hero = 0,
        Monster = 1,
        Boss = 2,
    }

    public abstract class Entity
    {
        protected long _iEntityIndex = 0;
        protected StateMachine _stateMachine = null;
        protected EntityData _data = null;

        public EntityData Data { get => _data; }

        public virtual void Init(object data) { }

        public abstract string GetEntityID();

        public StateType GetState()
        {
            if (null != _stateMachine && null != _stateMachine.CurState) 
            {
                return _stateMachine.CurState.StateType;
            }

            return StateType.Idle;
        }

        public abstract EntityType GetEntityType();

        public virtual void ChangeState(StateType state, Object data = null) 
        {
            if (null != _stateMachine) 
            {
                _stateMachine.ChangeState(state, data);
            }
        }

        public virtual void OnUpdate() 
        {
            if (null != _stateMachine) 
            {
                _stateMachine.Update();
            }
        }

        public virtual void Attack(Entity target) { }

        public virtual void OnHurt(int deltaHp) { }

        public abstract bool IsDeath();

        public virtual void OnDestroy() { }
    }
}

5、EntityManager

EntityManager 用于管理游戏中的所有 Entity ,包含 添加、移除 和 更新。亦可用于 Entity 间的互相通讯(下面的代码没有实现,只需简单添加 OnMessager(string entityID, object data = null){ },即可)。

using FSM.Entities;
using FSM.Interface;
using System;
using System.Collections.Generic;

namespace FSM.Manager
{
    public class EntityManager : IManager
    {
        private static readonly Lazy<EntityManager> _instance =
            new Lazy<EntityManager>(() => new EntityManager());

        public static EntityManager Instance
        {
            get { return _instance.Value; }
        }

        private long _iEntityIndex = 0;
        public long EntityIndex { get { return ++_iEntityIndex; } }

        private Dictionary<string, Entity> _dicEntity = null;
        private List<string> _listKey = null;

        private EntityManager() { }

        public void Init()
        {
            _dicEntity = new Dictionary<string, Entity>();
            _listKey = new List<string>();
        }

        public void AddEntity(Entity entity)
        {
            string key = entity.GetEntityID();
            if (!_dicEntity.ContainsKey(key))
            {
                _dicEntity.Add(key, entity);
                _listKey.Add(key);
            }
            else
            {
                Console.WriteLine("Entity is exist id = " + key);
            }
        }

        public void Update()
        {
            int len = _listKey.Count;
            for (int i = 0; i < len; i++)
            {
                string key = _listKey[i];
                Entity entity = _dicEntity[key];
                if (!entity.IsDeath())
                {
                    entity.OnUpdate();
                }

                // 移除死亡的对象
                if (entity.IsDeath())
                {
                    _listKey.RemoveAt(i);
                    len--;
                    i--;
                    _dicEntity.Remove(key);
                }
            }
        }

        public void RemoveEntity(string key)
        {
            if (_dicEntity.ContainsKey(key))
            {
                Entity entity = _dicEntity[key];
                entity.OnDestroy();

                _dicEntity.Remove(key);
                _listKey.Remove(key);
            }
        }

        public List<Entity> GetMonsters()
        {
            List<Entity> list = new List<Entity>();
            int len = _listKey.Count;
            for (int i = 0; i < len; i++)
            {
                string key = _listKey[i];
                Entity entity = _dicEntity[key];
                if (entity.GetEntityType() != EntityType.Hero)
                {
                    list.Add(entity);
                }
            }

            return list;
        }

        public Entity GetHero()
        {
            Entity hero = null;
            int len = _listKey.Count;
            for (int i = 0; i < len; i++)
            {
                string key = _listKey[i];
                Entity entity = _dicEntity[key];
                if (entity.GetEntityType() == EntityType.Hero)
                {
                    hero = entity;
                    break;
                }
            }

            return hero;
        }

        public void Clear()
        {
            _dicEntity.Clear();
            _listKey.Clear();
        }

    }

}

6、Demo

Demo 流程:

  • 创建 Hero 和 Monster
  • Hero 和 Monster 互相攻击
  • Monster 死亡后,Boss 登场
  • Hero 逃逸
using FSM.Data;
using FSM.Entities;
using FSM.Manager;
using System;
using System.Threading;

namespace FSM
{
    class Program
    {
        static void Main(string[] args)
        {
            EntityManager.Instance.Init();

            // Add Hero
            Hero hero = new Hero();
            InitData initData = new InitData(100, 10, 10,
                EntityManager.Instance.EntityIndex);
            hero.Init(initData);
            EntityManager.Instance.AddEntity(hero);

            // Add Monster
            Monster monster = new Monster();
            InitData monsterInitData = new InitData(20, 2, 2,
               EntityManager.Instance.EntityIndex);
            monster.Init(monsterInitData);
            EntityManager.Instance.AddEntity(monster);

            bool createBoss = false;

            while (!hero.IsDeath())
            {
                EntityManager.Instance.Update();
                if (monster.IsDeath() && !createBoss)
                {
                    createBoss = true;
                    Boss boss = new Boss();
                    InitData bossInitData = new InitData(1000, 50, 50,
                       EntityManager.Instance.EntityIndex);
                    boss.Init(bossInitData);
                    EntityManager.Instance.AddEntity(boss);
                }
                Thread.Sleep(500);
            }
            Console.WriteLine(" 游戏结束");
            Console.ReadKey();
        }
    }
}

运行结果

运行结果

参考资料:
[1] 游戏人工智能编程案例精粹(修订版)
[2] Finite-state machine:https://en.wikipedia.org/wiki/Finite-state_machine

欢迎关注个人公众号,实时推送最新博文!
在这里插入图片描述

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Unity有限状态机(Finite State Machine,FSM)是一种行为控制模式,用于管理游戏对象或系统的不同状态和转换之间的关系。在Unity中,我们可以使用Unity提供的有限状态机类图(StateMachineBehaviour)来实现这个模式。 有限状态机类图是一个基于状态转换的图形表示,其中包含了状态(State)、转换(Transition)和事件(Event)等元素。状态表示游戏对象或系统可能处于的不同状态,转换表示从一个状态到另一个状态的条件,事件表示触发状态转换的事件。 在Unity中,我们可以通过创建一个继承自StateMachineBehaviour的脚本,并将其附加到游戏对象的Animator组件上,来实现有限状态机。在这个脚本中,我们可以定义不同的状态和转换,并实现相应的行为逻辑。 例如,我们可以定义游戏角色的不同状态,如待机状态、行走状态和攻击状态等。我们可以定义转换条件,比如当角色受到攻击时,会从待机状态转换到攻击状态。然后,我们可以实现相应的行为逻辑,比如在攻击状态下执行攻击动作。 使用有限状态机类图可以帮助我们更好地管理复杂的游戏逻辑和行为,并使代码更加清晰和可维护。它允许我们根据不同的状态和条件来组织和执行行为,从而实现更灵活和可扩展的游戏逻辑。 ### 回答2: Unity的有限状态机(Finite State Machine,简称FSM)类图是描述Unity游戏中的状态机行为的图示。有限状态机是一种用来描述对象或者系统状态转换的模型。 在Unity中,有限状态机常常用于控制游戏中的角色行为、敌人AI游戏流程等。FSM类图主要包括状态(State)、转换(Transition)和行为(Action)三个主要元素。 状态(State)表示对象或系统可能处于的一种情况,比如游戏中的"待机"、"移动"、"攻击"等状态。每个状态都有对应的行为和相应的转换条件。 转换(Transition)表示状态之间的切换条件,它决定了对象或系统从一个状态切换到另一个状态的时机。转换通常基于一些触发条件,比如玩家的输入、特定事件的发生等。 行为(Action)表示在每个状态下对象或者系统执行的具体操作。行为可以是移动、攻击、播放动画等。每个状态都有对应的行为,这些行为会随着状态的切换而改变。 FSM类图中可以看到每个状态(State)被表示为一个矩形框,框内包含了状态名称和该状态下的行为(Action)。不同状态之间的转换(Transition)用箭头表示,箭头上标注着触发条件。 通过使用FSM类图,开发人员可以更清晰地描述游戏中的状态转换逻辑,方便编写对应的代码实现。同时,FSM类图也可以作为开发文档的一部分,方便团队成员之间的沟通和理解。 总之,Unity的有限状态机类图是一种用来描述游戏对象或系统状态转换逻辑的图示,通过状态、转换和行为的关系,可以清晰地表示游戏中的角色行为、敌人AI等复杂逻辑。 ### 回答3: Unity有限状态机(Finite State Machine)是Unity游戏引擎中的一种常用设计模式,用于管理游戏对象的各种状态和状态转换。下面是一个示例的Unity有限状态机类图。 在该类图中,有三个主要的类:状态机(StateMachine)、状态(State)和转换(Transition)。状态机有限状态机的核心,它包含了状态的集合和状态之间的转换关系。 状态是实际执行的动作,例如待机、行走、攻击等。每个状态都继承自状态基类,并实现了状态所需的行为。在状态机中,每个状态都有一个唯一的标识符。 转换是状态之间的切换条件。例如,当玩家按下某个按钮时,从待机状态切换到行走状态。转换条件是由转换类实现的,每个转换类都包含了触发转换的条件和转换的目标状态。 状态机中的状态和转换通过字典保存,并通过标识符进行访问和切换。状态机还有一个当前状态变量,用于表示当前游戏对象的当前状态。 实际使用时,可以根据具体游戏的需求,扩展状态机和状态。例如,可以添加更多的状态和转换,并在状态之间设置复杂的转换条件和逻辑。通过有限状态机设计,可以使游戏对象的状态切换更加清晰、灵活和简单。 总之,Unity有限状态机类图展示了状态机、状态和转换之间的关系和组织方式,通过使用该类图,可以更好地管理游戏对象的各种状态和状态之间的转换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值