Unity人工智能学习—躲避规则障碍一

第一部分:效果预览先上效果图。这一部分将要讲的是智能躲避规则的障碍物。其实,这是最终现实效果,在算法中实际上使用到了

Unity人工智能学习—躲避规则障碍一_第1张图片

一个隐藏的碰撞检测盒子,这个盒子是从智能体延伸出的。如图所示:保持红色长方形区域不被碰撞就可以躲避障碍物了。检测盒的宽度等于智能体的包围半径

Unity人工智能学习—躲避规则障碍一_第2张图片

它的长度正比于智能体的当前速度,即它移动的越快,检测盒子就越长。如图所示为实际的效果图:

Unity人工智能学习—躲避规则障碍一_第3张图片

第二部分:算法讲解

1、寻找最近的相交点

检测与障碍物的相交点的过程是相当复杂的,所以下面逐步讲解。

(1)、 智能体只考虑哪些在检测盒内的障碍物。初始的时候,要将游戏世界中所有的障碍物都迭代到内存中,并标记哪些在检测盒内的障碍物以作进一步分析。
(2)、 然后把所有已经标记的障碍物都转换到智能体的局部空间。转换坐标后,那些 x 坐标为负值的物体将不被考虑,所以问题就变得简单多了
(3)、 接下来必须要检测障碍物是否和检测盒重叠。使障碍物的包围半径扩大检测盒宽度的一半。然后测试该障碍物的 y 值是否小于这个值(即障碍物的包围半径加上检测盒宽度的一半)。如果不小于,那么该障碍物将不会与检测盒相交,可以不处理如图所示是上述三种情况的示意图:
Unity人工智能学习—躲避规则障碍一_第4张图片
(4)、 此时,只剩下那些与检测盒相交的障碍物了。接下来我们找出离智能体最近的相交点。再一次在局部空间中计算,第三步中扩大了障碍物的包围半径。用简单的线 圆周相交测试方法可以得到被扩大的圈和 x 轴的相交点。如图所示:
Unity人工智能学习—躲避规则障碍一_第5张图片
有两个相交点(我们不用但系和交通工具 相切的情况,交通工具将擦过障碍物)。注意如下情况,在智能体前 面可能有个障碍物,但和智能体相交在交通工具的后部,如上图所示的障碍物A,和交通工具的后部有个交点 该算法将不处理这种情况,只考虑在x轴上的交点。此算法测试所有剩下的障碍物,从中找出一个有最近(正 的)相交点的障碍物。在讲解如何计算操控力之前,先列出实现躲避障碍物算法中的相关代码:
 Vector2 AI_ObstacleAvoidance()
    {
        //检测盒子长度正比于智能体的速度
        m_dDboxLength = MinDetectBoxLength + (Plane.Speed/Plane.MaxSpeed) * MinDetectBoxLength;
        detectBox.rectTransform.localScale = new Vector3(1, m_dDboxLength / 50, 0);
        //Debug.Log(m_dDboxLength);
        TagNeighbors(m_dDboxLength);
        TagObstacles ClosestIntersectiongObstacle = null;
        Vector2 LocalPosOfClosestObstacle = new Vector2();//记录被转换的局部坐标。
        float DisttToClosestIP = float.MaxValue;
        foreach (TagObstacles obj in TagObstaclesList)
        {
            //处理被标记的障碍物
            if (obj.isTag)
            {
                Debug.Log(obj.gameObject.name + "Posx:" + obj.postion.x + "y:" + obj.postion.y);
                Vector2 LocalPos = PointToLocalSpace(ref obj.postion, Plane.vHeading,  Plane.vSide, Plane.LocalPosition);//这个plane的一定要是局部坐标

                //  Debug.Log("x:"+LocalPos.x+"y:"+LocalPos.y);
                //如果在导弹的局部坐标的正X轴即处理,否则就丢弃,因为在负X轴的都是在导弹的后面
                if (LocalPos.x >= 0)
                {
                    //如障碍物带局部坐标系的X轴的距离小于障碍物的半径+检测盒宽度的一半,那么可能相交
                    float ExpandedRadius = obj.gameObject.GetComponent().radius + m_dDboxWidth / 2;
                    if (Mathf.Abs(LocalPos.y) < ExpandedRadius)
                    {
                        //现在做线/圆周相交测试。圆周的中心是(cX,cY)
                        //相交点的公式是x=cX-Mathf.Sqrt(r^2-cY^2)此时y=0。我们只需要看x的最小正值,因为那是最近的相交点
                        float cX = LocalPos.x;
                        float cY = LocalPos.y;
                        //我们只需要计算一次上面等式的开方
                        float SqrtPart = Mathf.Sqrt(ExpandedRadius * ExpandedRadius - cY * cY);
                        float ip = cX - SqrtPart;
                        if (ip <= 0)
                        {
                            ip = cX + SqrtPart;
                        }
                        //                       Debug.Log("ip:" + ip + "DisttToClosestIP:" + DisttToClosestIP);
                        //测试是否这是目前为止最近的,如果是,记录这个障碍物和它的局部坐标。
                        if (ip < DisttToClosestIP)
                        {

                            DisttToClosestIP = ip;
                            ClosestIntersectiongObstacle = obj;
                            LocalPosOfClosestObstacle = LocalPos;
                        }
                    }
                }

            }
        }
        //如果找到一个相交的障碍物,计算一个远离它的操作力,分别计算在局部空间这个力的X和Y方向的分量
        Vector2 SteeringForce = new Vector2();
        if (ClosestIntersectiongObstacle != null)
        {
            //导弹距离障碍物越近,在局部空间Y方向的制动力就会越强,计算这个影响因子
            float multiplier = 1.0f + (m_dDboxLength - LocalPosOfClosestObstacle.x) / m_dDboxLength;
            //计算这个力:障碍物的包围半径减去其在局部坐标空间的Y值
            SteeringForce.y = (ClosestIntersectiongObstacle.gameObject.GetComponent().radius - LocalPosOfClosestObstacle.y) * multiplier;
            //添加一个在局部空间X方向的制动力来作为刹车,它正比于导弹到障碍物的距离
            float BrakeingWeight = 0.2f;
            SteeringForce.x = (ClosestIntersectiongObstacle.gameObject.GetComponent().radius - LocalPosOfClosestObstacle.x) * BrakeingWeight;

            //  Debug.Log("x:" +SteeringForce.x + "y:" + SteeringForce.y);
        }
        //最后将这个控制力的局部坐标转换到世界坐标空间

        return VectorToWorldSpace(ref SteeringForce, Plane.vHeading, Plane.vSide);
    }

这里面有一个从世界坐标到局部坐标的转换以及如何调整智能体的运动方向使其平滑规避障碍物,这些将会在
下一节讲解


你可能感兴趣的:(Unity,Unity,人工智能,ai,算法)