Unity人工智能AI编程知识

群组行为:
模拟鸟群行走或人群行走过程的称之为群组行为
分散 、队列 、 聚集
分散:在群体内,个体必须与其他个体之间保持一定的距离,当小于那个距离时,就要分散开来。
实现原理:确定需要分散的范围separationDistance,获取在该范围内的个体,计算出当前个体与(当前个体的周围个体)的相反作用力的合力,即 合力 += (当前个体位置-其他个体位置); 
但是,由于我们是想要当前个体位置与其他个体位置的距离越小,产生的相反作用力越大,所以 相反作用力= (当前个体位置 -其他个体位置).normalized / (当前个体位置-其他个体位置).magnitude ,这样就能够距离越小,产生的相反作用力越大,反之越小。
相反作用力的总体意思就是 当前个体位置-其他个体位置,即得到一个 其他个体位置 指向 当前个体位置的 三维向量
这个相加之后的力就是分散合力,至于我上面说的当距离越小,作用力越大是根据实际需求来决定的。
这样,我们得到了一个分散合力


队列:在群体内,个体的朝向必须保持与队列朝向一致
实现原理:
队列朝向的获取:队列指的是 当前个体的周围的个体的平均朝向,那么我们一样根据上面的相同的原理获取到周围个体,然后周围个体的朝向就是transform.forward 将这些周围个体朝向 全部相加起来,再求平均,即得到了队列朝向。
而我们想要的目的是 将当前朝向 逐渐 朝向 队列朝向。那么我们需要加一个力,这个力叫队列趋向力
我们将队列朝向向量-当前个体朝向向量,当前个体朝向向量是transform.forward,这样获取到一个队列趋向力,为什么会要这样算?因为队列朝向向量-当前个体朝向向量是根据物理上的四边形原则,即合力、分力的一个原理来得出的。如果我们只是将队列朝向向量当做队列趋向力的话,这样变化幅度很小,而我们直接计算出一个能够让当前个体受力变为队列朝向的分力,变化幅度就很大。若还是不理解的话,可以看下物理的合力、分力的解释以及四边形原则,在这里 队列朝向就是合力,当前个体原本的受力和队列趋向力都是分力,2个分力合起来就是一个合力,即 当前个体原本的受力(当前朝向向量)+队列趋向力 = 队列朝向向量 ,这样我们也能推导出: 队列趋向力=队列朝向向量-当前朝向向量。
这样,我们就得到了一个队列趋向力

聚集:与分散相反原理。
我们在分散不发生的情况下才产生聚集,一样地获取周围个体,但是我们不是计算出聚集合力什么的,而是计算出周围个体的中心点,中心点计算方法:周围个体的位置之和/周围个体数量
然后,中心点-当前个体位置=当前个体位置 朝向 中心点 的向量,然后这个向量就是我们的聚集力
如果对聚集力的大小有点不满意的话,推荐:将聚集力的大小设置为当前速度的大小


经过以上说明,我们得到了三个力:分散合力、队列趋向力、聚集力,这三个力都可以分别设置一个权重,来调整它们的影响,加权重的意思就是给力乘以一个值
然后,我们将这三个力相加得到了一个 最终合力,我们是用这个最终合力来影响当前物体的
根据牛顿第二定律:F=ma  , 故  a= F/m  (F是最终合力,a是加速度(m/s^2) m是质量(kg) )
所以,在Update里面我们可以通过这个公式计算出 最终合力对物体产生的加速度a
当前速度+=a*Time.deltaTime 这样处理加速度对当前速度的影响,当前速度是一个三维向量
我们通过使用Quaternion.Slerp来逐渐旋转当前个体的朝向,面向当前速度的朝向
移动的话可以用Translate 直接朝向当前个体的前方forward来移动,速度大小为当前速度大小


FSM有限状态机:
个人理解:FSM就是一个控制状态切换的集中处理器,用一个FSMSystem管理着所有FSMState,每个FSMState(状态)都有它自身对应的转换事件(可有多个)(Transition(枚举))。
FSM根据传递进来的Transition信息,将当前状态转换为其它状态,这个转换成功条件必须是:当前状态中存在该转换事件Transition,并且转换事件对应的状态ID存在于FSM管理器中,这样才会发生转换。
每次转换时,都会触发(当前状态离开事件)和(转换后的当前状态进入事件),每个状态都有进入事件和离开事件。
注意:当前状态必须存在转换条件,并且该转换条件对应的状态必须存在于FSM管理器中!
例如:StartState、RunState、EndState 三个状态存在于FSM管理器
StartState中注册了Transition.GoToRunState转换条件,该转换条件对应的State是RunState,由于我们的状态是通过字典(键值对形式)保存在FSM状态机中的,所以我们还要有一个枚举StateID来标识每种状态,而转换条件对应的就是StateID了,那么上面的RunState的StateID属性为StateID.Run。这样StartState就存在了一个转换条件,这个转换条件也有了对应的StateID,我们把这个放入字典保存起来,key是Transition,value是StateID。
而在游戏一开始默认是StartState,那么我们就应该要有一个初始化状态机的当前状态的方法,将当前状态设置为StartState,ID为StateID.Start, 并且调用StartState的进入事件,我们状态必须要重写进入事件和离开事件,因为使得每种状态的不同就是它们的作用,不然FSM将毫无意义。
在我们给FSM状态机一个转换消息时,例如:Transition.GoToRunState,那么FSM会这样做:
1.从当前状态(StartState)中,寻找该转换条件GoToRunState对应的状态ID即StateID.Run
2.拿到StateID后,我们在FSM中保存的状态字典内 通过StateID寻找出对应的状态,即RunState
3.找到后,先调用 当前状态的离开事件(当前状态是StartState),然后将当前状态转为RunState,ID也要变,然后调用当前状态的进入事件(当前状态是RunState)。

补充:
Act事件,它是每个状态必须重写的事件,会在FSM状态机的Update中调用,FSM状态机只会调用当前状态的Act事件,Act事件相当于状态运行中的各种行为。
Reason事件,它是每个状态必须重写的事件,同样会在FSM状态机的Update调用,与Act的行为区别是它集中处理了各种转换事件的发生,就是说在什么情况下会触发什么转换条件,从而让FSM状态机自己去管理自己的状态转变。
但是,当FSM状态机不继承于Monobeviour时,Act事件和Reason事件也可能并非由状态机自身去调用,而是由创建状态机的那个脚本的Update去调用状态机的Update方法。注意,状态机不继承于MonoBehaviour的时候是这样用的,当状态机继承于Monobehaviour就不需要创建状态机自身了。
至于状态机继不继承Monobehaviour,可以自己看着办,个人认为继承于Monobehaviour不太好。


感知系统:

        视觉:

        1、全方位无死角视觉,实现原理:利用Vector3.Distance()判断距离是否在视觉范围内。(不常用)

        2、半圆弧视觉:利用Vector3.Distance()判断距离是否在视觉范围内,并且用Vectoe3.Angle(机器人前方,从机器人指向玩家的向量)得到夹角(<90°),判断这个夹角是否在视觉角度/2内,若在视觉角度/2内,则认为是看到了,否则反之。半圆弧视觉和全方位无死角视觉的区别就在于多了一个Angle判断。这个视觉看不到看人 还会受到障碍物影响,在确认玩家在视觉范围内时,还要发射一条射线判断是否有障碍物挡着,若射线检测到的是玩家,则表明玩家在视野内,若碰到的是障碍物则不会看到玩家,也就不会触发看到玩家后的操作了。

        听觉:

        1、玩家跑动Bool变量,当玩家跑动时,为true,否则为false

        2、听觉最大距离:利用Unity寻路系统Nav,将2个点之间的最短距离计算出,然后判断这个最短距离是否 小于 听觉最大距离,若小于,则去判断玩家跑动Bool变量是否为true,若为true就表示听到了。这2点的意思是 机器人位置和玩家位置,因为我们的听觉是会受到障碍物干扰的,所以在计算2点距离的时候就不能使用简单的Vector3.Distance了,而是使用导航系统Nav的计算方法,或者根据你自己的寻路系统方法来计算2点的最短路径。

        后期说明,如何利用Nav导航系统计算最短路径问题TODO。

A*寻路:

           1、制作虚拟地图(美工管的),虚拟地图一般是用(一个类class或者结构体struct)去描述地图上一个点的信息,例如:

一个点需要有 坐标x,y ,是否为障碍物bool标志位,父节点,F,G,H,(基本都要这些)   

            2、讲解A*寻路的一个方程式:F=G+H

            简单地说明一下,F越小越好,代表着寻找出的路径会比较短,而不是最短。其中,G是确定的,H是预判的。

            该结点G的计算方法= 父节点G值 + 父节点到达该结点的距离(直接用Vector3.Distance计算出)

            H值的计算方法可以有多种:例如:第一种是直接 是 该结点与终点的X之差的绝对值与Y之差的绝对值 之和。

                                                                第二种是 直接Vector3.Distance计算出 该结点与终点的距离。

                                                                其他的方法也有 ,自己想到什么是什么,这个会影响到最终寻找出的路径。

            这里,因为H值的计算方法是人为设定的,可能靠谱可能不靠谱,故F值也不一定是最完美的,所以A*算法计算出的路径也不一定是最短的,而只能说是比较短的。

            3、A*算法需要的5个东西,上面已经说了1个,虚拟地图map[,] 我们通过把2D/3D的物体虚拟化为一张地图,这张地图的每个点都是一个信息体,即Point,里面记录着一些信息,初始化虚拟地图的时候 我们只会将x,y值和障碍物bool初始化。

            


行为树:

猜你喜欢

转载自blog.csdn.net/qq_39574690/article/details/80958838