前些日子学了射线寻路的方法,通过往前发出一条射线,如果射线击中的障碍物位置点的法线在射线的右侧,则往右侧转向,如果在左侧,则往左侧转向。如下图所示:
//以下方法为通过对比射线射中的法线来进行躲避障碍物
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;
}
这样子确实能躲避一些简单的障碍物,但是我们很快就可以发现,会有一些问题:当碰到两个障碍物在一起形成夹角时,很有可能导致在这个角落里来回转动,再也走不出这个角落了
所以我自己想了一些方法来解决这个问题,增加了一个判定是否进入死胡同模式:
1、初步简单的认定,当再一次碰到之前遇到的障碍物时(不包括刚刚碰到的),进入死胡同模式;
2、进入死胡同模式后,怪物只有一个行为,就是尽量沿一个方向贴着墙壁走逃离死胡同;
3、需要确认是否离开死胡同,离开死胡同后恢复怪物的正常行为
怎么判定怪物离开死胡同了呢,我这里用了两个条件,一个是怪物与目标之间必须不能再碰到死胡同的障碍物,怪物的前进方向与目标方向之间的夹角必须小于90度。
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算法,更好的解决这些问题。而且代码也写得比较乱,以后还得多下功夫。