文章目录

  • 状态模式(有限状态机)
    • 结构
    • 实现(有限状态机)
    • 应用场景
    • 优缺点
    • 与其他模式的关系

状态模式(有限状态机)

状态模式是一种对象型模式,他将复杂的逻辑判断提取到不同状态对象中,允许状态对象在其内部状态发生改变时,改变其行为。状态的改变和各个状态的行为是状态模式的核心

这里模式就需要谈到一个游戏中经常使用的状态模式的形式。它就是有限状态机,有限状态机是作为对象不同状态的管理而使用的(游戏AI也经常使用有限状态机)。其主要思想是程序在任意时刻仅可处在有限的几个状态中。在任意状态中,程序行为将随状态的改变,发生改变。而状态和状态之间的切换都是预先设计好的。

预先定义的状态切换规则称为转移

结构

说明

  • 状态接口 - 定义状态的行为和状态切换
  • 具体状态 - 实现状态的行为和状态切换
  • 系统 - 负责状态的切换和状态的实际调用以及状态的管理。

实现(有限状态机)

这里将实现一个有限状态机,状态机分为四个部分

  • 状态枚举
  • 状态接口及状态类
  • 条件接口及条件类
  • 状态系统

我们使用了泛型来增强有限状态机的复用性,使用工厂来创建状态机(避免复杂的创建过程,停留在调用端来污染代码)

为了方便考虑,我只枚举了 Idle(闲置)和 Chase(追击)两种状态


  • 第一部分

状态枚举

    public enum StateId{Idle,Chase,}
  • 第二部分

状态接口

    public interface IState<T>where T : Enum{//获得对应状态类的IdT Id { get; }//状态进入前,调用此函数void OnEnterState();//状态时,调用此函数void OnUpdateState();//状态退出前,调用此函数void OnExitState();//状态转换函数,通过此函数判断是否转移状态bool TransitionState(out T id);}

敌人抽象状态

    public abstract class EnemyState : IState<StateId>{private readonly StateId _id;private readonly ITransitionState<StateId> _transitionState;protected EnemyState(StateId id, ITransitionState<StateId> transitionState){_id = id;_transitionState = transitionState;}public StateId Id => _id;public virtual void OnEnterState() { }public abstract void OnUpdateState();public virtual void OnExitState() { }public bool TransitionState(out StateId id) => _transitionState.Transition(out id);}

之所以多出来这个抽象类,是为了将状态条件逻辑和状态行为逻辑分开,使得子类无需关注状态转移,只关注实现即可。而状态的转移实现转交给条件类完成(实现状态判断和状态行为实现的分离)。

具体状态类(Idle,Chase)

    public class EnemyIdleState : EnemyState{public EnemyIdleState(ITransitionState<StateId> transitionState) : base(StateId.Idle, transitionState){}public override void OnUpdateState(){}}
    public class EnemyChaseState : EnemyState{        private float _chaseSpeed;private float _chaseRange;private GameObject _go;private GameObject _chaseTarget;//物理缓存private Collider[] _colliders = new Collider[1];public EnemyChaseState(float chaseSpeed, float chaseRange, GameObject go, ITransitionState<StateId> transitionState) : base(StateId.Chase, transitionState){_go = go;_chaseSpeed = chaseSpeed;_chaseRange = chaseRange;}public override void OnEnterState(){_chaseTarget = null;int num = Physics.OverlapSphereNonAlloc(_go.transform.position, _chaseRange,_colliders, 1 << LayerMask.NameToLayer("Player"));if (num != 0) _chaseTarget = _colliders[0].gameObject;}public override void OnUpdateState(){//移动var position = _go.transform.position;position += _chaseSpeed * Time.deltaTime * (_chaseTarget.transform.position - position).normalized;_go.transform.position = position;//旋转_go.transform.LookAt(_chaseTarget.transform);}public override void OnExitState(){_chaseTarget = null;}}
  • 第三部分

条件接口

    public interface ITransitionState<T>where T : Enum{bool Transition(out T id);}

具体条件接口(IdleTransition,Chase Transition)

    public class EnemyIdleStateTransition : ITransitionState<StateId>{//自身游戏对象private GameObject _go;//侦察范围private float _scoutingRange;//侦察范围间隔(每帧调用,不利于程序性能)private readonly float _scoutingTime = 0.2f;private float _currentTime;public EnemyIdleStateTransition(GameObject go, float scoutingRange){_scoutingRange = scoutingRange;_go = go;}public bool Transition(out StateId id){_currentTime += Time.deltaTime;if (_currentTime >= _scoutingTime){_currentTime = 0f;if (Physics.CheckSphere(_go.transform.position, _scoutingRange, 1 << LayerMask.NameToLayer("Player"))){id = StateId.Chase;return true;}}id = StateId.Idle;return false;}}
    public class EnemyChaseStateTransition : ITransitionState<StateId>{//脱离追击的距离private float _outChaseDistance;//自身游戏对象private GameObject _go;//脱离范围间隔(每帧调用不利于程序性能)private readonly float _outChaseTime = 0.2f;private float _currentTime;public EnemyChaseStateTransition(GameObject go, float outChaseDistance){_outChaseDistance = outChaseDistance;_go = go;}public bool Transition(out StateId id){_currentTime += Time.deltaTime;if (_currentTime >= _outChaseTime){_currentTime = 0f;if (!Physics.CheckSphere(_go.transform.position, _outChaseDistance, 1 << LayerMask.NameToLayer("Player"))){id = StateId.Idle;return true;}}id = StateId.Chase;return false;}}
  • 第四部分

有限状态机系统类

    public class FsmSystem<T>where T : Enum{private Dictionary<T, IState<T>> _stateDic;private T _currentStateId;private IState<T> _currentState;public T Id => _currentStateId;public FsmSystem(){_stateDic = new Dictionary<T, IState<T>>();}public void Add(IState<T> state){if (state == null) return;if (_stateDic.ContainsKey(state.Id)) return;_stateDic.Add(state.Id, state);}public void Remove(T id){if (!_stateDic.ContainsKey(id)) return;_stateDic.Remove(id);}public bool Enable(T id){if (!_stateDic.ContainsKey(id)) return false;_currentStateId = id;_currentState = _stateDic[id];_currentState.OnEnterState();return true;}public void Update(){if (_currentState.TransitionState(out T id))TransferState(id);_currentState.OnUpdateState();}//转移状态函数private void TransferState(T id){if (!_stateDic.ContainsKey(id)) return;_currentState.OnExitState();_currentState = _stateDic[id];_currentStateId = id;_currentState.OnEnterState();}}

工厂类

    public class FsmFactory{public static FsmSystem<StateId> CreateEnemyFsm(GameObject go, float chaseRange, float chaseSpeed, float outChaseRange){var fsm = new FsmSystem<StateId>();//创建条件,并添加条件所需对应参数var idleStateTransition = new EnemyIdleStateTransition(go, chaseRange);var chaseStateTransition = new EnemyChaseStateTransition(go, outChaseRange);//创建状态,并添加状态所需参数,而且将条件与状态绑定var idleState = new EnemyIdleState(idleStateTransition);var chaseState = new EnemyChaseState(chaseSpeed, chaseRange, go, chaseStateTransition);fsm.Add(idleState);fsm.Add(chaseState);fsm.Enable(StateId.Idle);return fsm;}}

调用端

    public class StateExample : MonoBehaviour{//追击速度[SerializeField] private float _chaseSpeed = 3.0f;//追击范围[SerializeField] private float _chaseRange = 4.0f;//脱离追击距离[SerializeField] private float _outChaseRange = 5.0f;//此时状态[SerializeField] private StateId _stateId;private FsmSystem<StateId> _system;private void Awake(){_system = FsmFactory.CreateEnemyFsm(gameObject, _chaseRange, _chaseSpeed, _outChaseRange);}private void Update(){_system.Update();_stateId = _system.Id;}}

将玩家对象设置为 Player 层

效果图

由于不会作动图只能将就这看吧,代码没有问题,效果也还不错。

应用场景

  • 游戏敌人AI
  • 需要根据不同状态进行改变,有不同行为。
  • 如果某个类需要根据成员变量的当前值改变自身行为,需要大量的判断条件时,可以使用状态模式。

优缺点

优点

  • 将状态分离,单一职责原则
  • 简化条件的判断

缺点

  • 实现过于繁琐
  • 类数量过多,容易造成系统复杂

与其他模式的关系

  • 状态模式被视作策略模式的扩展,状态模式本身和策略模式是有区别,策略类之间是独立的,互不干涉。而状态之间需要进行切换,状态与状态之间是依赖的。但本质都是基于组合的机制。
  • 桥接模式状态模式的接口非常相似。 桥接模式关注在实现化和抽象化上,不同实现化互不关联,实现各自业务逻辑,抽象化对象将其组合。本质两种都是基于组合机制实现的。即将工作委派给对象。
  • 状态机的创建可以由工厂模式来完成。
  • 状态机可以使用桥接模式来分开状态转移和状态实现。

状态模式 - Unity(有限状态机)相关推荐

  1. 【游戏设计模式】之三 状态模式、有限状态机 Unity版本实现

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接: http://blog.csdn.net/poem_qianmo/article/details/52824776 作者:毛星云(浅 ...

  2. 状态模式、有限状态机 Unity版本实现

    游戏开发过程中,各种游戏状态的切换无处不在.但很多时候,简单粗暴的if else加标志位的方式并不能很地道地解决状态复杂变换的问题,这时,就可以运用到状态模式以及状态机来高效地完成任务.状态模式与状态 ...

  3. 【游戏设计模式】之三 状态模式、有限状态机

    转载自:https://blog.csdn.net/poem_qianmo/article/details/52824776 游戏开发过程中,各种游戏状态的切换无处不在.但很多时候,简单粗暴的if e ...

  4. 【游戏设计模式】之三 状态模式 有限状态机 Unity版本实现

     本系列文章由@浅墨_毛星云 出品,转载请注明出处.    文章链接: http://blog.csdn.net/poem_qianmo/article/details/52824776  作者:毛星 ...

  5. 【C++】50.编程中切换状态的【有限状态机】

    对于有限状态机的知识,这里推荐浅墨毛星云大佬的博客[游戏设计模式]之三 状态模式.有限状态机 & Unity版本实现,博客里做了具体的代码讲解. 状态机是表示有限个状态以及在这些状态之间的转移 ...

  6. 有限状态机(使用状态模式C++实现)

    最近在研究怪物的AI,由于怪物的AI是使用有限状态机来实现的.所以就查看关于有限状态机的东西.根据这几天的查看资料,知道了有限状态机是计算机科学中一个很重要的概念.而且有限状态机是一个抽象的概念,具体 ...

  7. 状态模式 有限状态机_有限状态机解释

    状态模式 有限状态机 The finite state machine (FSM) is a software design pattern where a given model transitio ...

  8. Java 有限状态机 (设计模式——状态模式)

    Java 有限状态机 (设计模式--状态模式) 编写代码的时候,有时会遇见较为复杂的swith...case...和if...else...语句.这一刻有时会想到状态机,用有限状态机替换swith.. ...

  9. 游戏开发设计模式之状态模式 有限状态机 c#委托事件(unity3d 示例实现)

    命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 原型模式:游戏开发设计模式之原型模式 & unity3d ...

最新文章

  1. JSTL标签库学习笔记
  2. redis系列:基于redis的分布式锁
  3. jQuery实现布局高宽自适应
  4. 使用ASP.NET Core 3.x 构建 RESTful API - 3.3.1 HTTP状态码
  5. 《事实:用数据思考,避免情绪化决策》笔记
  6. vlc 在ubuntu 14下的linux版本编译
  7. 递增的整数序列链表的插入
  8. (六)使用ResNet50迁移学习进行COVID-19诊断:从头开始构建深度学习网络
  9. EXTJS学习笔记:grid之分组实现groupingview
  10. 扩展频谱--直接序列扩频
  11. 计算机后端维护,机房智能交通后台系统运行维护内容.doc
  12. Java中9种常见的CMS GC问题分析与解决
  13. 利用JavaScript批量删除QQ空间说说(只需一个浏览器)
  14. 免费公测 标贝声音理解,检测声音性别和年龄
  15. Kotlin高仿微信-项目实践58篇
  16. QA:GMS认证、CTS测试、GTS测试、谷歌认证、安卓认证,Android
  17. 美少年为“苹果”编程挣钱 12岁办软件开发公司
  18. ElasticSearch-TransportClient客户端操作Es(五)
  19. 如果由你来设计 12306.cn,你会怎么设计?
  20. 深度学习是怎么回事?有什么用?

热门文章

  1. TLS/SSl相关的攻击漏洞及检测方法大杂烩!
  2. 一网打尽英文关联逻辑词,为公式运行保驾护航
  3. 脱光衣服待着就能减肥,当真有这好事?
  4. HCIP-7.0华为交换机技术基础学习
  5. cmder win10 安装配置
  6. C语言用regcomp、regexec、regfree和regerror函数实现正则表达式校验
  7. spring整合servlet
  8. Excel函数 - 提取固定字符中的文字
  9. windows下mingw64编译glog
  10. html+css点亮灯泡