欢迎光临散文网 会员登陆 & 注册

基础 | 自治智能体----类鸟群(三)

2020-03-21 17:40 作者:有木乘舟  | 我要投稿

本系列为笔者初学c/c++和游戏AI开发的学习过程,算法为主,不涉及到具体的游戏开发软件学习(如unity,虚幻4等),若有错误请在评论区留下批评意见。

  • 语言:c/c++ (11及以上)

  • 平台:macOS mojave

  • 编译器:vs Code / g++

二、一群乱飞的鸟

2.1 一只乱飞的鸟

  我们知道一只现实中的鸟,会在遇到天敌的时候加速飞开,或者藏到鸟群中,也会四处盘旋以寻找果腹的食物。这也是我们的程序最后要实现的功能,但如此多复杂的功能,要一步实现显然是非常困难的。

  所以在那之前,我们后退一大步,先实现一个简单的小目标----创建一只乱飞的鸟,这样实现起来就容易多了。之后,我们在慢慢的教会这只“笨鸟”更多实用的“技能”。

  首先,我们要将鸟抽象成计算机可以理解的对象,这将由一个类来实现。暂时先抛开鸟的大小、外形等因素,要抽象出空间中的一只鸟,至少需要三个属性:

  • location:位置向量,描述一只鸟在平面空间中的位置

  • velocity:速度矢量,描述一只鸟的当前飞行速度

  • acceleraton:加速度矢量,描述一只鸟的当前加速度

  有了以上三个属性,我们就可以利用加速运动公式计算出一只鸟的空间位置和运动路径。除此之外,我们在第一章节中学到,类鸟群Boids可归纳出三种主要的行为模式:

  • separation: 领域内若有其他boids存在,该函数计算出一个距离,使当前boid与其他boids保持距离。

  • alignment: 该函数计算出一个速度矢量,使其与其他boids保持相同的速度,并维持队列。

  • cohesion: 计算出附近boids的质心,并使当前boid前往该质心。

  这三种主要的行为模式,是由一些次要的操控行为(Steering Behaviors)组成:

  • seek:靠近,控制boid靠近一个目标

  • flee:离开,控制boid离开一个目标

  • pursuit:追逐,控制“猎食者”追捕一个目标,该行为只有“猎食者”才拥有

  • evade:逃避,控制boid逃离一个目标,功能与flee类似,但其作用主要用来躲避“猎食者”的追捕,因此需要额外的功能

  • wander:徘徊,当环境安全时,一只鸟会在世界里游荡嬉戏,随机运动

  • obstacle avoidance:避开障碍,当遇到障碍物的时候,绕开障碍物

  以上方法便可以创造出一群足够智能的机器鸟,当然我们不会一下子就全都实现。在实现一只足够智能的鸟之前,我们先搞出一只只会徘徊乱飞的“笨鸟”,如图9:

图9:Boid类

2.2 Vector2D

  在讲解具体的代码实现之前,我们有必要了解一下一个十分重要的组件Vector2D类

  简单来讲,该类存储智能体的位置信息(x和y),并提供必要的向量运算工具,项目中涉及到向量运算的地方,全都由该工具类来实现。

  该工具类具体的实现和用法,请各位小伙伴下载代码查看注释即可。

2.2 seek靠近

  seek方法是最基础的方法,该函数输入一个目标位置向量,并返回一个操控智能体到达目标位置的力(表现为加速度a)。

  我们知道力是一个向量,由大小和方向组成,因此只要得到了智能体的受力,就可以很容易的计算出其运动轨迹来。如图10:

图10:智能体受力情况
  1. 计算预期的速度,该速度是从智能体到目标的向量,大小为智能体的最大速度。只需将目标位置向量减去智能体位置向量,再将其标准化,就能得到该速度的方向。之后再乘以智能体最大速度,就能计算出最终的速度向量。

    预期速度 = ||(目标位置 - 智能体位置)|| * 最高速度

  2. 计算智能体受力,即智能体在当前速度和预期速度的影响下,下一时刻的受力情况,也就是运动轨迹。

    智能体受力 = 预期速度 - 当前速度

  我们首先计算预期速度(蓝色箭头),再将预期速度减去当前速度,就可以得到智能体受力(红色箭头)。红色箭头即为我们所需的受力。代码如图11:

图11:seek方法

2.3 wander徘徊

  wander方法产生一个操控力,驱使智能体在环境中随机走动。

  我们采用Reynolds的解决方案,通过一个圆来计算智能体的运动路径。

  在智能体周围画一个圆圈,圆心与智能体位置相同。接着,在圆圈上选一个目标位置,让目标产生一个随机抖动值,使其离开圆圈。然后,将偏离圆圈的目标映射回圆圈上,把目标连同圆一起向右移动一定距离。最后,智能体朝新的目标位置移动。

图12:运动路径

  具体步骤如下:

  • 对目标增加一个随机位移wanderJitter,使其偏离圆圈。这里在具体实现的时候,要算上时间差值,使运动更平滑。这里,wanderTarget目标要先初始化,即先把圆圈画出来。

红色为移动后的目标
  • 将偏移后的目标,重新投影到圆圈上。计算公式:新目标 = 旧目标单位向量 * 圆半径

红色为移动后的目标
  • 向右移动目标的位置,距离为wanderDistance,使其远离智能体。

红色为移动后的圆和目标
  • 接着,计算出此时目标的位置向量,将其投影到世界空间中,即智能体运动的环境。

  • 最后,通过seek方法计算出智能体到目标的受力,然后通过速度运动公式就可以计算出智能体到目标的运动路径。

图13:wander方法
图14:更新智能体位置

2.4 增量时间

  由于篇幅限制,一些非核心部分的工程实现就不多细讲,但有一个地方需要特别注意。

  实际动手用Unity3D或UE做过游戏的小伙伴,一定记得游戏制作过程中,控制物体运动时,需要给速度添加一个增量时间 deltaTime来使物体的运动更加的平滑。

  在这个项目中,我们一样也需要考虑到这个因素。在这里,我们使用SFML系统模块中自带的时钟函数来实现该功能。

  代码中的elapsed变量就是增量时间,我们需要在窗口每一帧渲染的时候,计算出该变量的值,然后传递给每个智能体。

图15:增量时间

2.5 结果演示

参考:

  • 《游戏人工智能编程案例精粹》

  • 相关代码下载    https://github.com/linpeijie/GameToy

基础 | 自治智能体----类鸟群(三)的评论 (共 条)

分享到微博请遵守国家法律