趣说游戏AI开发:对状态机的褒扬和批判

0x00 前言

因为临近年关工作繁忙,已经有一段时间没有更新博客了。到了元旦终于有时间来写点东西,既是积累也是分享。如题目所示,本文要来聊一聊在游戏开发中经常会涉及到的话题——游戏AI。设计游戏AI的目标之一是要找到一种便于使用并容易拓展的的方案,常见的一些游戏AI方案包括了有限状态机(FSM)、分层有限状态机(HFSM)、面向目标的动作规划(GOAP)以及分层任务网络(HTN)和行为树(BT)等等。下面我们就来聊一聊比较有代表性的游戏AI方案——状态机。

0x01 有限状态机(FSM)

有限状态自动机 (Finite State Machine,FSM)是表示有限多个状态以及在这些状态(State)之间转移(Transition)和动作(Action)的数学模型。有限状态机的模型体现了两点:

  1. 状态首先是离散的:某一时刻只能处于某种状态之下,且需要满足某种条件才能从一种状态转移到另一种状态。
  2. 然后状态总数是有限的。
    此处输入图片的描述

从它的定义,我们可以看到有限状态机的几个重要概念:

  • 状态(State):表示对象的某种形态,在当前形态下可能会拥有不同的行为和属性。
  • 转移(Transition):表示状态变更,并且必须满足确使转移发生的条件来执行。
  • 动作(Action):表示在给定时刻要进行的活动。
  • 事件(Event):事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态。

而状态机便是用来控制对象状态的管理器。在满足了某种条件或者说在某个特定的事件被触发之后,对象的状态便会通过转换来变成另外一种状态,而对象在不同的状态之下也有可能会有不同的行为和属性。
当然,有限状态机的应用范围很广,但是显然游戏开发是有限状态机最为成功的应用领域之一。除了游戏AI的实现可以依靠有限状态机之外,游戏逻辑以及动作切换都可以借助有限状态机来实现。因此游戏中的每个角色或者器件或者逻辑都有可能内嵌一个状态机。

0x02 HFSM分层有限状态机

如果我们仔细观察一个有限状态机的话,可以发现它在逻辑结构上是没有层次的,如果和行为树来做对比的话可以发现这一点十分明显。在行为树中,节点是有层次(Hierarchical)的,子节点由其父节点来控制。例如行为树中有一种节点叫做“序列(Sequence)节点”,它的作用是顺序执行所有子节点(如果某个子节点失败返回失败,否则返回成功)。而将行为树的这个优势应用到有限状态机上,分层有限状态机HFSM便诞生了。

分层的好处

那么引入了分层之后的HFSM到底带来了什么好处呢?
最大的好处便是在一定程度上规范了状态机的状态转换,从而有效地减少了状态之间的转换。
举一个简单的小例子:例如RTS游戏中的士兵。如果逻辑没有层次上的划分,那么我们对士兵所定义的若干状态,例如前进、寻敌、攻击、防御、逃跑等等,就需要在这些状态之间定义转移,因为它们是平级的,因此我们需要考虑每一组状态的关系,并维护一大堆没有侧重点的转移。
如果在逻辑上是分层的,我们就可以将士兵的这些状态进行一个分类,把几个低级的状态归并到一个高级的状态中,并且状态的转移只发生在同级的状态中。
例如高级状态包括战斗、撤退,而战斗状态中又包括了寻敌、攻击等几个小状态;撤退状态中又包括了防御、逃跑这几个小状态。
686199-20160103232925339-1051063527.png
总而言之,分层状态机HFSM从某种程度上规范了状态机的状态转移,而且状态内的子状态不需要关心外部状态的跳转,这样也做到了无关状态间的隔离。

0x03 有限状态机的实现

那么到底如何实现一个有限状态机呢?主要有两种方式来实现,即集中管理控制以及模块化管理。具体来说,这两种方式的实现如下:

  1. 使用switch语句:所有的状态之间的转移逻辑全都写在一个部分,需要根据不同的分支来判断转移条件是否符合。
  2. 使用状态模式(State Pattern):一种常见的设计模式。在状态模式中,我们为每个状态创建与之对应的类,这样就将状态转移的逻辑从臃肿的switch语句中分散到了各个类中。

了解了有限状态机大体上可以分为这两种实现方式,那么接下来我们就具体来看一看这两种方式是如何实现的。

switch语句

在实现有限状态机时,使用switch语句是最简单同时也是最直接的一种方式。这种方式的基本思路是为状态机中的每一种状态都设置一个case分支,专门用来对该状态进行控制。
此处输入图片的描述
上图是一个具体的使用有限状态机实现游戏AI的场景,描述的是一个游戏单位的AI,下面我们就使用switch语句来实现图中的状态机。

switch (state)  
{// 处理状态Waiting的分支case State.Waiting: // 执行等待wait();// 检查是否有可以攻击if (canAttack()){// 当前状态转换为AttackingchangeState(State.Attacking);}// 若不可攻击,则检查是否有可以移动else if (canMove()) { // 当前状态转换为MovingchangeState(State.Moving)}break;// 处理状态Moving的分支case State.Moving: // 执行动作movemove();// 检查是否可以攻击敌人if (canAttack()) {// 当前状态转换为AttackingchangeState(State.Attacking);}// 若不可攻击,则检查是否可以等待else if (canWait()) {// 当前状态转换为WaitingchangeState(State.Waiting);}break;// 处理状态Attacking的分支case State.Attacking: // 执行攻击attackattack();// 检查是否可以等待if (canWait()) {// 当前状态转换为WaitingchangeState(State.Waiting);}break;
}

通过这个小例子,我们可以看到使用switch语句实现的有限状态机的确可以很好的运行。不过我们还可以发现这种方式在实现状态之间的转换时,1.检查转换条件以及2.进行状态转换的代码都是混杂在当前的状态分支中来完成的,这样就会导致代码的可读性降低甚至会增加日后的维护成本。
这是因为在每个具体的状态下,都需要检查多个具体的转换条件,对符合条件的还需要转移到新的具体的状态,这样的代码是难以维护的,因为它们需要在具体的情况下处理具体的事物。即便我们将检查转换条件和进行状态转换的代码分别封装成两个专门的函数FuncA(检查转换条件)和FuncB(进行状态转换),switch语句中各个具体状态的代码可能会更加清晰。但是随着逻辑复杂度的增加,FuncA和FuncB这两个函数本身的复杂度可能也会增加,甚至最后变得臃肿不堪。

状态模式

当控制一个对象状态转换的条件表达式过于复杂时,把状态的判断逻辑转移到一系列类当中,可以把复杂的逻辑判断简单化。因此,使用状态模式来实现状态机虽然不如直接使用switch语句来的直接,但是对于状态更易维护也更易拓展。下面我们就来看一看状态模式中的角色:

  1. 上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态的实例,将与状态相关的操作(1.检查转换条件;2.进行状态转换)交给当前的具体状态对象来处理。
  2. 抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
  3. 具体状态(Concrete State):实现抽象状态定义的接口。
下面,我们就按照这三个角色来实现上一小节图中的状态机吧。
context类

public class Context
{private State state;public Context(State state){this.state = state;}public void Do(){state.CheckAndTran(this);}
}

抽象状态类:

public abstract class State
{public abstract void CheckAndTran(Context context);
}

具体状态类

public class WaitingState : State
{public override void CheckAndTran(Context context){//执行等待动作Wait();//检查是否可以攻击敌人if (canAttack()){// 当前状态转换为Attackingcontext.State = new AttackingState();}// 若不可攻击,则检查是否有可以移动else if (canMove()) { // 当前状态转换为Movingcontext.State = new MovingState();}}
}
...

虽然看似状态模式缓解了使用switch语句那种代码臃肿、可读性维护性差的问题,但是状态模式并非没有自己的缺点。可以看出状态模式的使用必然会增加类和对象的个数,如果使用不当将导致程序结构和代码的混乱。

0x04 褒扬和批判

在游戏开发中使用状态机显然不失为一种不错的选择,首先它的概念并不复杂,其次它的实现也十分简单而直接。但它的缺点却也十分明显,例如难以复用,因为它往往需要根据具体的情况来做出反应,当然当状态机的模型复杂到一定的程度之后,也会带来实现和维护上的困难。如何选择,可能就是一个仁者见仁智者见智的问题了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/543780.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sencha touch调试时Please close other application using ADB: Monitor, DDMS, Eclipse

1、运行——cmd—— netstat -aon|findstr "5037" 2、打开任务管理器,查看所有进程 显示进程pid(文件-查看)--查找pid7740的结束。转载于:https://www.cnblogs.com/taoshengyujiu/p/5099588.html

l和l_L&T的完整形式是什么?

l和&lL&T:Larsen和Toubro (L&T: Larsen and Toubro) L&T is an abbreviation of Larsen and Toubro. It is an Indian multinational conglomerate corporation with international networks and operations. It is dynamically engaged in …

CRT的完整形式是什么?

CRT:阴极射线管 (CRT: Cathode Ray Tube) CRT is an abbreviation of Cathode Ray Tube. Cathode Ray Tube is a vacuum tube that accommodates one or more than one electron filled guns and a phosphorescent screen, which is used in television and convent…

推送证书

2019独角兽企业重金招聘Python工程师标准>>> 推送证书 1 openssl pkcs12 -in CertificateName.p12 -out CertificateName.pem -nodes 转换文件上传 2证书有效期 openssl x509 -in xxx.pem -noout -dates —反馈 notBeforeDec 12 07:42:27 2015 GMT notAfterDec 11…

Dubbo学习总结(4)——Dubbo基于Zookeeper实现分布式实例

入门实例解析 第一&#xff1a;provider-提供服务和相应的接口 创建DemoService接口 [java] view plaincopyprint? <span style"font-size:18px;">package com.unj.dubbotest.provider; import java.util.List; /** * 定义服务接口&#xff0c;该…

[转载]PhotoShop性能优化

现在随着Photoshop版本越来越高功能也越来越强大&#xff0c;而往往强大的功能需要电脑有好的配置运行&#xff0c;比如HDR、图像合成或者3D和视频等类似的功能&#xff0c;还有处理比较大尺寸的图像时&#xff0c;如果电脑配置不够强往往非常卡&#xff0c;这时我们就要好好设…

0.1uf与47uf并联_UF是什么形式?

0.1uf与47uf并联UF&#xff1a;超滤 (UF: Ultrafiltration) UF is an abbreviation of Ultrafiltration. It is a kind of membrane filtration which is used in UF water purifiers. Through a hollow fiber threaded semi-permeable membrane, the water is made to proceed…

机器学习相关——协同过滤

在现今的推荐技术和算法中&#xff0c;最被大家广泛认可和采用的就是基于协同过滤的推荐方法。本文将带你深入了解协同过滤的秘密。下面直接进入正题 1 什么是协同过滤 协同过滤是利用集体智慧的一个典型方法。要理解什么是协同过滤 (Collaborative Filtering, 简称 CF)&#x…

InfoQ中文站2015年度优秀社区编辑评选揭晓

\又到了年终岁末&#xff0c;在过去的一年里&#xff0c;InfoQ网站的月独立UV接近130万&#xff0c;月PV突破200万&#xff0c;每周独立访问用户接近30万&#xff0c;网站访问量过万的文章超过60篇。每月活跃的数十位社区编辑为InfoQ的内容生产贡献着力量。正是这点点汇聚的星光…

【设计模式】—— 访问者模式Visitor

对于某个对象或者一组对象&#xff0c;不同的访问者&#xff0c;产生的结果不同&#xff0c;执行操作也不同。此时&#xff0c;就是访问者模式的典型应用了。 应用场景 1 不同的子类&#xff0c;依赖于不同的其他对象 2 需要对一组对象&#xff0c;进行许多不相关的操作&#x…

ruby宝石区块链最新消息_Ruby宝石| Ruby工具

ruby宝石区块链最新消息Ruby宝石 (Ruby Gems) Every language has its package manager which helps it by providing libraries and a standard format to distribute Ruby program. It is a type of tool which is developed to easily facilitate the installation of Gems.…

最小生成树prim (c++ 已大改)

2019独角兽企业重金招聘Python工程师标准>>> #include <iostream> #include <vector> #include <set> #include <map> #include <initializer_list> #include <memory> template<typename T> class Graph{private:std::m…

前端接入HTTP协议浅析

【摘要】&#xff1a;本文整理并简要分析了HTTP协议的交互过程和内容格式&#xff0c;包括HTTP请求、HTTP应答的头域和实体内容&#xff0c;HTTP 1.0与HTTP 1.1的差异&#xff0c;并举例说明了Chunked编码的工作过程原理。1、HTTP协议简介浏览器和Web服务器之间一问一答的交互过…

互联网传真 传真指令_传真的完整形式是什么?

互联网传真 传真指令传真&#xff1a;传真 (FAX: Facsimile) FAX is an abbreviation of "Facsimile". 传真是“传真”的缩写 。 It is commonly written and spoken as FAX. It is a telephonic transmission of a scanned copy of text and images printed on a p…

C#使用七牛云存储上传下载文件、自定义回调

项目需要将音视频文件上传服务器&#xff0c;考虑并发要求高&#xff0c;通过七牛来实现。 做了一个简易的压力测试&#xff0c;同时上传多个文件&#xff0c;七牛自己应该有队列处理并发请求&#xff0c;我无论同时提交多少个文件&#xff0c;七牛是批量一个个排队处理了。 一…

Linux下DRBD配置

一、什么是DRBD1、简介 Distributed Replicated Block Device(DRBD)是一个用软件实现的、无共享的、服务器之间镜像块设备内容的存储复制解决方案。数据镜像&#xff1a;实时、透明、同步&#xff08;所有服务器都成功后返回&#xff09;、异步&#xff08;本地服务器成功后返回…

vue3实现本地开发使用的px转换成vw,px转换成rem方法整理

前言&#xff1a; 项目中如果想本地开发使用px&#xff0c;但是界面上线以后界面是自适应的效果,可以有多种方式来实现效果。 一、px转成vw 1、安装&#xff0c;安装成功后&#xff0c;node_modules 会新增这两个插件包 npm i postcss-px-to-viewport-8-plugin 2、新增 post…

airplay2协议是什么_什么是AirPlay?

airplay2协议是什么AirPlay (AirPlay) AirPlay is released by Apple in the year 2004. It allows the easy exchange of audios without the use of any wired technique between the two devices. It was previously termed as AirTunes and later got its name changed to …

微信支付开发(5) 订单查询

本文介绍微信支付中订单查询功能的实现。 作者&#xff1a;方倍工作室 地址&#xff1a;http://www.cnblogs.com/txw1958/p/wxpay-order-query.html 一、订单查询 因为某一方技术的原因&#xff0c;可能导致商户在预期时间内都收不到最终支付通知&#xff0c;此时商户可以通过该…

python饼形图_Python | 饼形图

python饼形图A pie plot or a pie chart is a circular statistical graphic technique, in which a circle is divided into slices with respect to numerical proportion. In a pie chart, the arc length, central angle, and area of each slice, is proportional to the …