一个建立完整游戏AI的事例:第一部分(作者:Geoff Howland)

来源:互联网 发布:领导力网络课测验答案 编辑:程序博客网 时间:2024/06/05 20:36



人工智能(AI)一直是一种后台的开发技术,可是在未来的游戏中对于AI的开发比重会不断增加。如果你的游戏的AI是跟不上水准,玩家对你的游戏的评价会是过时的你会因此而感到不舒服。
游戏AI不仅仅是神经网络和学习系统,以及复杂的数学算法。虽然可以使用这些,但游戏的AI主要是建立一种环境和外在的表现通过游戏中的对象体现出来。游戏AI要有行为性,而不是科学性。
一个建立游戏AI的关键是要明确游戏最终表现的结果是什么。这将全部展现在游戏玩家眼前。如果这无法表达出来,那最好还是别说了。
以下的一个例子和探讨是关于即时战略游戏的,其中很多的概念可以应用于其它类型的游戏。所有的代码是用C语言:

状态机和有限状态机:
一个有限状态机(FSM)是一个系统,其中包含一定数量的操作状态。一个真实世界里一个开关会有开和关两种状态,或一个闹钟会到时响起。有响起,记时和定时三种状态。任何系统其中如果包含有限的可以被定义成一种状态的能力,则可以将其定义为一种有限状态机。
有限状态机在许多程序中应用,并且明白如何有效的建立游戏AI是和建立一个游戏世界一样困难,所以要尽可能的详细和简明扼要。
如何使用有限状态机
在游戏中使用FSM有许多目的,但其中一个比较难处理的是如何规范一个行为模型来模拟人类的行为,这是相当困难的。想模拟人类的行为经常会出错,已经有很多的游戏中出现这种情况这些都被游戏玩家们发现并指出来了。而且还是在非常详细的FSM系统下。
当你设计一些非常精确的关于人类学习和思考的系统时,你可以用一种简单的方式判定,想象作为一个人,你会怎样做出选择。当学习了更多关于AI的决定和学习系统后,仍然要用这种方法,这样往往会很简单的设计出来而不是那样的具有科学上地精确性。
不要因此产生误解和对神经网络系统,遗传算法以及其它人工智能算法的抵触,,只是要选择一些明智的解决途径和有趣的算法,作为更好的解决方法,并且达到最佳效果。用你要达到的最终目的来衡量,并作出选择,并且不要认为是唯一的。

游戏状态机:
建立一个满意的游戏环境,意味着要尽量考虑许多玩家可能注意到的详细的环节。更多情况下要预先计划并测试,这样当玩家在游戏环境中不断移动时会沉浸于其中。
在你的所有的游戏状态中,至少要有两个状态机,以便游戏顺利进行。第一个状态机是处理游戏界面的,包含游戏是否暂停,玩家游览世界的模式如果有很多种,可以列出来,有些玩家可以或不可以看到的,以及其它一些标记可以做成特殊的界面。
struct GameLevelState {
  int alert;            // Alert status of enemies //
  struct Positionshot;  // Position of last shot fired //
  int shotTime;         // Game cycle last shot was fired //
  int hostage;          // Hostage rescued //
  int explosives;       // Explosives set or not //
  int tank;             // Tank destroyed //
  int dialogue;         // Dialogue variable //
  int complete;         // Mission completed //
 };


灵活性:
保持游戏AI的灵活性是非常重要的。如果你把规则定的很标准,你将更加容易将其进一步扩充。理解设计一个游戏AI是和设计一种内部线程是很相似的,你需要尝试在此之上建立它们。
建立一个好的AI的目标是看起来所有的游戏单位都是在环境中彼此之间相互影响的进行一些行为。没有这些,对游戏对象的适应是很难的而且游戏的界面也会很糟。如果玩家没有控制游戏单位的感觉和获得游戏单位的返回信息,则玩家只会在一个游戏界面中毫无目的的点来点去,所有对该游戏的沉浸感将无影无踪。这意味着玩家将对此感到毫无乐趣和厌倦。
下面是一个你可能会提供给玩家的游戏信息的一个结构:
struct Character {
  struct Positionpos;               // Map position //
  int screenX, screenY;             // Screen position //
  int animDir, animAction, animNum; // Animation information, action and animation frame number //
  int rank;                         // Rank //
  int health;                       // Health //
  int num;                          // Number of unit //
  int group;                        // Group number //
  int style;                        // Style of unit (elf, human) //
  struct AnimationObject animObj;   // Animation object for complex animations //
};


以下是一些变量的定义:
pos变量定义了游戏单位的位置坐标,screenX, screenY是为了在屏幕上显示游戏单位的一些相关信息如健康值和是否被选择。
animDir, animAction 和 animNum都是用来定义游戏单位的动画状态,这些都会在屏幕上显示。
rank 和 health变量是游戏单位的属性信息。
num变量是游戏单位在游戏单位数组中的序号。当调用游戏单位的相关信息时,用来选择相应的游戏单位,不需要直接给出结构的地址。
group变量决定游戏单位属于哪个编组,作为一个游戏单位会属于一个编组中。只有当单位死亡后才不会属于一个组中。
style 和 animObj是更多的游戏单位显示信息。
建立更高级的部分:
当你的游戏单位按照一定的路线进行时,更进一步,是时候建立更多信息使它们好象在过真正的生活。
你需要思考是什么让游戏单位不断重复的去做某件事。你想让他们有情感吗?你想让他们恐惧,奔跑,想一个妇女一样购物吗?
你需要继续添加变量来决定情感状态。最好的理解游戏单位的情感成分是,先在自己身上做一些设想。为了建立一种人类的反映机制,你需要了解一个真实的人是怎样做反映的。
在另一方面,几乎是相对于人类的情感,这里提供了一个符合的经验并不基于现实,但可以用于挑战玩家。除了使游戏单位拥有人类天性,你还需要想想,是否有理智让你的游戏单位去做这些反映。问题是你必须做出结论,是否要添加这些,或是只是简单的结束,用单调的反映机制,虽然做起来很容易,或只用随机的方式解决。所有这些可能毁了一个好游戏,所以要多做些功能进去,但有一点很重要,就是要让玩家玩一辈子也不知道是怎么弄出来的!

建立组:
建立组还是不建立?
如果你做了一个第一人称射击游戏,当然用不到。可是,在即时战略游戏中或一个玩家控制不止一个游戏单位的游戏中,有个问题要知道:
你需要你的单位在一种协和的方式中行动吗?
如果你回答是,这里有个好主义,即建立组。如果回答不,则好处是你不必将信息传给组里的每个游戏单位,尤其是在一个目标点改变时。
多个游戏单位的协同行动,比如巡逻建筑,可以控制在一个中央目标点附近。而不必让每个游戏单位独自行动,并检测是否碰到其他的游戏单位然后在返回从新确定位置。
组可以保持它们的共同结构,所以发布一个指令只需要花费向一个游戏单位发布指令一样的时间。更重要的是你将建立一些容易理解和读取的方式。操控25个游戏单位并让其相互通信变的非常简便。
建立在组上的行动,对于障碍回避和检测变的容易,并且寻找路径的时间也减少许多,这在有大批的游戏单位的状态下是很有效的。
大场面:
组织好你的组,就想建立游戏AI一样,想象调动所有的游戏单位做最后的进攻。这里有个想法是建立一块区域作为监控中心,以便游戏单位可以快速的被发现。想法是不复制任何数据,你只需在一个资源中寻找数据,这个区域将建立在最合理的位置,其他逻辑扩展的功能也应该在合理的位置。
根据我地经验,我决定这样做区分,任何必须和游戏单位有关联的将作为一种附加物放到游戏对象数据结构中。
这意味着游戏对象在行动的过程中,或在他们所在的物理坐标上做某些行为时,一直不会有任何信息处理,比如动画序列和世界坐标。这样做意味着一个游戏单位必须属于一个组才有能力移动或改变行为。如果他们是独立的,则整体做为一个组对待。
大批单位中的一个单位:
我们最终找到一个组,以便进行统一的行动,一个坐标系统是建立很多小块,这尤其在游戏单位寻路时很重要,所以当我们需要组结束编队,并单独行动,需要建立如下结构:
struct GroupUnit {
  int unitNum;                  // Character Number //
  struct Unit *unit;            // Unit character data //
  struct Positionwaypoint[50];  // Path in waypoints for units //
  int action[50];               // Actions by waypoints //
  int stepX, stepY;             // Step for individual units, when in cover mode //
  int run, walk, sneak, fire, hurt, sprint, crawl;          // Actions //
  int target;                   // Targets for units //
  struct Position targetPos;    // Target Position //
};

变量含义:
unitNum是组中游戏单位的数量。如果组中最多有10个单位,这里将有10个位置。
unit是一个指针,指响unit结构,将保存游戏单位当前的坐标,健康值和其他信息。一个游戏单位的生命值标记将指示该游戏单位是否受伤或正在和其他游戏单位进行沟通,或其它一些信息。
waypoint数组是游戏单位移动的路径序列。所有行动和waypoints是唯一指定在GroupUnit结构里的,取决于组是否编队。
action数组包含了移动后的行为。允许建立更多的指令序列,控制游戏单位隐秘行动或迅速移动,你还可以想象其他的战略性的行为。
stepX, stepY是一个速度信息;每祯的移动所有的游戏对象。恰当使用可以适合所有物理性的事件发生时。在一个普通的系统下通常减少处理时间。
run, walk, sneak是处理不同的游戏单位状态。并不是动画,但是动作状态可以做标记,还有很多状态,可以互相影响。
target,targetPos用来指定攻击目标,和自身位置坐标。敌人的坐标,和健康值和其它属性一样,可以用敌人的游戏单位号来查找,但为了方便读取我决定保留一个本地的敌人坐标的拷贝。
群体智能:
我们的最终目的是要有一个中心位置为尽量多的数据尽可能限制单位数和线程数并使问题简单话。来看以下的数据结构:
struct Group {
  int numUnits;              // Units in group //
  struct GroupUnit unit[4];  // Unit info //
  int formation;             // Formation information for units and group //
  struct Position destPos;   // Destination (for dest. circle) //
  int destPX, destPY;        // Destination Screen Coords //
  struct Position wayX[50];  // Path in waypoints for group //
  float formStepX, formStepY;  // Formation step values for group movements //
  int formed;                // If true, then find cover and act as individuals, otherwise move in formation //
  int action, plan;          // Group action and plans //
  int run, walk, sneak, sprint, crawl, sniper;   // Actions //
  struct Position spotPos;   // Sniper Coords //
  int strategyMode;          // Group strategy mode //
  int orders[5];             // Orders for group //
  int goals[5];              // Goals for group //
  int leader;                // Leader of the group //
  struct SentryInfo sentry;  // Sentry List //
  struct AIStateaiState;     // AI State //
};

numUnits提交组里游戏单位的数量,游戏单位数组存储GroupUnit信息。这个特殊的组只允许有最大4个游戏单位数
formation是一个是否编队的标记,决定编队的类型。可以编队成柱形,楔形,菱形。只需定义相应的编号。
destPos 和 destPX,destPY是提供到达目的地的信息迅速对玩家的操作做出反映。waypoints和steps是独立单位的控制方式,在编队中的所有游戏单位都将有相同的速度,这样才可以保持对型。不需要为每个单位改变速度。
formed是决定是否游戏单位存在编队或单独行动。如果有对型则所有游戏对象将进行同样的操作。如果有原因不能用一种方式移动,比如遭到进攻或必要的信息中断,遇到障碍,游戏单位就各自移动。
actions是独立的行为指定,比如如果有个狙击手在组中,你不可能让他统一行动。这就是将游戏单位区分的逻辑组,并控制狙击手在组的级别下行动。这种设计就是在最佳设计结构思想下做出的。
strategyMode是一个灵活变量决定怎样对敌人做出反应。是勇敢的,有谋略的,还是防御性的,还有投降?用一种简单的存取形式用来控制基本的响应,是一种好方法减去许多独立游戏单位和组的状态计算。更好的提供玩家控制,玩家可以设置不同组来做不同的行动。
orders 和 goals数组指明命令和目的,在命令和目的数据库中规定的行为要求多个组方便的作出行为反映。
sentry 和 aiState包含是否站岗和响应的AI状态值
合在一起:
现在已经有了一些组的结构,下面介绍如何使用:


一定要仔细的编写AI程序,要规范和灵活,这样才可以随意添加,方便调用每部分。思路是要有数据的组织架构,在一个函数里做一些有特殊性的功能。然后在其他地方调用。最后如果你的AI有问题,需要调试。则不需要检查每条语句,因为有一个特殊的函数,至少要有这种习惯。
 

原创粉丝点击