Unity中通过射线躲避障碍物寻路的一些初步探索

前些日子学了射线寻路的方法,通过往前发出一条射线,如果射线击中的障碍物位置点的法线在射线的右侧,则往右侧转向,如果在左侧,则往左侧转向。如下图所示:

Unity中通过射线躲避障碍物寻路的一些初步探索_第1张图片

//以下方法为通过对比射线射中的法线来进行躲避障碍物
if (Physics.Raycast (transform.position + Vector3.up * 0.5f, transform.forward, out hit, 10f, lmBarrier)) { //往该角色提高半米的位置的前方发射一条10米的射线,如果有射中障碍物层级的物体
    float rotateDir = Vector3.Dot (transform.right, hit.normal); //获取角色右方向与击中位置的法线的点乘结果,主要用于判断障碍物位置,是在角色左边还是右边
    Debug.DrawRay (transform.position + Vector3.up * 0.5f, transform.forward * 7, Color.red); 
    Debug.DrawRay (hit.point, hit.normal, Color.blue);
    if (rotateDir >= 0) {  //如果大于等于0
        transform.Rotate (transform.up * 90 * Time.deltaTime);  //则往顺时针方向以90度每秒的方向转动
        lookReTime = 0; //避免在躲避障碍物的时候,还会朝向玩家,易发生相反转向
    } else {
        transform.Rotate (transform.up * -90 * Time.deltaTime);  //则往逆时针方向以90度每秒的方向转动
	    lookReTime = 0; 
	}

这样子确实能躲避一些简单的障碍物,但是我们很快就可以发现,会有一些问题:当碰到两个障碍物在一起形成夹角时,很有可能导致在这个角落里来回转动,再也走不出这个角落了

Unity中通过射线躲避障碍物寻路的一些初步探索_第2张图片

所以我自己想了一些方法来解决这个问题,增加了一个判定是否进入死胡同模式:

1、初步简单的认定,当再一次碰到之前遇到的障碍物时(不包括刚刚碰到的),进入死胡同模式;

2、进入死胡同模式后,怪物只有一个行为,就是尽量沿一个方向贴着墙壁走逃离死胡同;

3、需要确认是否离开死胡同,离开死胡同后恢复怪物的正常行为

Unity中通过射线躲避障碍物寻路的一些初步探索_第3张图片

怎么判定怪物离开死胡同了呢,我这里用了两个条件,一个是怪物与目标之间必须不能再碰到死胡同的障碍物,怪物的前进方向与目标方向之间的夹角必须小于90度。

Unity中通过射线躲避障碍物寻路的一些初步探索_第4张图片

        RaycastHit hit;  //用来检测前方是否有障碍物

        if (!Physics.Raycast(transform.position + Vector3.up * 0.5f, player.position - transform.position, out hit, Vector3.Distance(player.position, transform.position), lmBarrier))
        {//如果与目标之间发出的射线没有碰到障碍物的话
            transform.LookAt(player);
            isDilemma = false;  //死胡同模式关闭
            lookReTime = 0;
            Debug.Log("haha,看你往哪跑");
        }
        else
        {
            if (!isDilemma)  //如果不处于死胡同模式,则进入
            {
                if (Physics.Raycast(transform.position + Vector3.up * 0.5f, transform.forward, out hit, rayDis, lmBarrier))
                { //往该角色提高半米的位置的前方发射一条10米的射线,如果有射中障碍物层级的物体
                    barrier03 = barrier02;  //barrier01-03这几个都是声明的GameObject类型
                    barrier02 = barrier01;
                    barrier01 = hit.transform.gameObject;
                    
                    if (barrier01 == barrier02)
                    { //如果接着碰上的是同一个
                        rotateDir = Vector3.Dot(transform.right, hit.transform.position - transform.position);
                        
                        if (rotateDir >= 0)
                        {
                            transform.Rotate(transform.up * -180 * Time.deltaTime);
                            lookReTime = 0; //避免在躲避障碍物的时候,还会朝向玩家,易发生相反转向
                        }
                        else
                        {
                            transform.Rotate(transform.up * 180 * Time.deltaTime);
                            lookReTime = 0; //避免在躲避障碍物的时候,还会朝向玩家,易发生相反转向
                        }
                        Debug.Log("马上就会找到你的");
                    }else if (barrier03 == barrier01)  //如果刚碰上的和之前碰上的是同一个,则触发死胡同模式
                    { //进入死胡同模式
                        barrier04 = barrier01.transform;
                        rotateDir = Vector3.Dot(transform.right, barrier04.position - transform.position);
                        if (rotateDir >= 0)
                        {  //如果障碍物在前进方向右侧
                            for (int i = 0; i < 72; i++) //快速找出障碍物的边缘,往其边缘行走
                            {
                                transform.Rotate(transform.up * 5);
                                if (!Physics.Raycast(transform.position + Vector3.up * 0.5f, transform.forward, out hit, rayDis, lmBarrier))
                                {
                                    break;
                                }
                            }
                        }
                        else
                        {
                            for (int i = 0; i < 72; i++)
                            {
                                transform.Rotate(transform.up * -5);
                                if (!Physics.Raycast(transform.position + Vector3.up * 0.5f, transform.forward, out hit, rayDis, lmBarrier))
                                {
                                    break;
                                }
                            }
                        }
                        isDilemma = true;  //开启死胡同模式
                        Debug.Log ("进入死胡同模式啦");
                    }
                }
            }
            else
            {
                rotateDir = Vector3.Dot(transform.forward, player.position - transform.position);
                if (Physics.Raycast(transform.position + Vector3.up * 0.5f, player.position - transform.position, out hit, rayDis, lmBarrier))
                {
                    //Debug.DrawRay(transform.position + Vector3.up * 0.5f, player.position - transform.position, Color.red);
                    if (hit.transform.gameObject != barrier04.transform.gameObject && rotateDir > 0)
                    {
                        isDilemma = false;
                        // Debug.Log("死胡同模式解除啦啦");
                        transform.LookAt(player);
                        lookReTime = 0;
                    }
                    if (Physics.Raycast(transform.position + Vector3.up * 0.5f, transform.forward, out hit, rayDis, lmBarrier))
                    {
                        if (hit.transform.gameObject != barrier01 && hit.transform.gameObject != barrier02)
                        {
                            isDilemma = false;
                            //Debug.Log("临时解除死胡同模式");
                        }
                    }

                }
                else if (rotateDir > 0)
                {
                    isDilemma = false;
                    //Debug.Log("死胡同模式解除啦啦");
                    transform.LookAt(player);
                    lookReTime = 0;
                }
                else
                {
                    rotateDir = Vector3.Dot(transform.right, barrier04.position - transform.position);
                    if (rotateDir >= 0)
                    {
                        for (int i = 0; i < 72; i++)
                        {
                            transform.Rotate(transform.up * 5f);
                            if (Physics.Raycast(transform.position + Vector3.up * 0.5f, transform.forward, out hit, rayDis, lmBarrier))
                            {
                                transform.Rotate(transform.up * -5f);
                                break;
                            }
                        }
                    }
                    else
                    {
                        for (int i = 0; i < 72; i++)
                        {
                            transform.Rotate(transform.up * -5f);
                            if (Physics.Raycast(transform.position + Vector3.up * 0.5f, transform.forward, out hit, rayDis, lmBarrier))
                            {
                                transform.Rotate(transform.up * 5f);
                                break;
                            }
                        }
                    }
                    //Debug.Log("什么时候能出死胡同啊");
                }


            }		
        }

虽然解决了这种夹角问题,但是面对较复杂情形时,依然显得非常笨拙,所以后期准备去学习一些AI算法,更好的解决这些问题。而且代码也写得比较乱,以后还得多下功夫。


你可能感兴趣的:(Unity3D)