阅读《Unity Game AI programming 》第6章后,感觉躲避障碍物算法不是很给力。为了研究和学习Unity,自己改良躲避障碍物的算法。当然,代码没有优化,不过没关系,抛砖引玉,记录思想,学习交流。
1.启用物理引擎,使用速度和力解决问题。而不是使用的角度和位置
2.加入沿着障碍物行走,即使障碍物宽度很大或物体向障碍物前进的速度过快,也不会发生穿墙而过现象。
3.为配合沿着障碍物行走,并且显得更自然,引入三个速度与障碍物的交互分区。
public void AvoidObstacles (ref Vector3 dir) { RaycastHit hit; //Only detect layer 8 (Obstacles) int layerMask = 1 << 8; //Check that the vehicle hit with the obstacles within it's minimum distance to avoid //print (transform.forward); //障碍物交互区 if (Physics.Raycast (transform.position, rb.velocity, out hit, minimumDistToAvoid, layerMask)) { alongWall = false; Vector3 hitNormal = hit.normal; hitNormal.y = 0.0f; //Don't want to move in Y-Space disToAvoid = hit.distance; //[5-minimumDistToAvoid]排斥区 if (disToAvoid > 5) { print ("排斥区"); if (rb.velocity.sqrMagnitude > 100) rb.AddForce (hitNormal * 10); } //[2-5.0]牵引区 else if (disToAvoid > 2f) { print ("牵引区"); rb.AddForce (Vector3.Cross (hitNormal, Vector3.up).normalized * 5); //return; } //[0,2]平行区 else { print ("平行区"); alongWall = true; rb.velocity = Vector3.Cross (hitNormal, Vector3.up).normalized * 10; alongHitNormal = -hitNormal; //return; } } //目标交互区 else if (Physics.Raycast (transform.position, dir, out hit, Vector3.Distance (transform.position, targetPoint), layerMask)) { if (alongWall) { if (!Physics.Raycast (transform.position, alongHitNormal, out hit, 2.0f, layerMask)) { print ("脱离平行区,开始转向"); rb.AddForce (dir * 10); } } else if(hit.distance>minimumDistToAvoid){ print("正常"); rb.velocity=dir*8; } } else { print ("畅通无阻"); rb.velocity = dir * 10; } }这个算法依旧存在缺陷,最大问题是运算量较大,并且细节不够丰富,导致物体移动行为不自然。
当逐渐逼近障碍物时,首先进入排斥区,对移动物施加障碍物平面法线方向的斥力。再次逼近改为施加平行于障碍物平面的引导力。若再次逼近则将移动物体的速度直接更改为平行于障碍物平面的速度。平行于障碍物平面的速度是用向量叉乘求得的,并且遵守左手坐标系。
当物体沿着障碍物移动时,则不做任何事情,直到脱离障碍物,开始施加向目标点力。若物体没有沿着墙壁前进,并且通向目标一定范围内没有障碍物。则直接更改速度。
若发现目标点畅通无阻则直接更改速度向其前进。
把此段代码引入到原书项目中,可test效果。有一个问题需要注意一下,由于移动物体时存在体积的,所以单纯以物体position进行射线检测会在拐角处发生碰撞,为了简化问题,开启移动物体isTrigger选项。
原文地址:http://blog.csdn.net/zjq2008wd/article/details/46047925