Unity Movement AI (二)

发表于2017-10-09
评论0 7k浏览

这个库有以下Movement AI :Arrive 抵达,Cohesion 凝聚,Collision Avoidance 碰撞避免,Evade 逃避,Flee 逃离,Follow Path 跟踪路径,Hide 隐藏,Interpose 插入(干预,管闲事),Offset Pursuit 偏移追求,Pursue 追求,Seek 寻求,Separation 分离,Velocity Match 速度匹配,Wall Avoidance 墙壁避免,andWander 漫游。


7 Evade 逃避;规避;逃脱

Evade 和 Flee 的区别,
首先 Evade 有使用到 Flee , 类似于 Pursue 使用 Seek 一样
真正的不同和意义是, 这个有预测的部分,根据目标的速度可以预测它的下一个位置在哪?
测试场景: 有意思,让 Seek 追着他, 然后 Evade 会逃脱

  1. public class EvadeUnit : MonoBehaviour  
  2. {  
  3.     public Rigidbody target;  
  4.   
  5.     private SteeringBasics steeringBasics;  
  6.     private Evade evade;  
  7.   
  8.     void Start()  
  9.     {  
  10.         steeringBasics = GetComponent<SteeringBasics>();  
  11.         evade = GetComponent<Evade>();  
  12.     }  
  13.   
  14.     void Update()  
  15.     {  
  16.         Vector3 accel = evade.getSteering(target);  
  17.   
  18.         steeringBasics.steer(accel);  
  19.         steeringBasics.lookWhereYoureGoing();  
  20.     }  
  21. }  

  1. [RequireComponent(typeof(Flee))]  
  2. public class Evade : MonoBehaviour  
  3. {  
  4.     /* // 未来预测的最大预测时间 */  
  5.     public float maxPrediction = 1f;  
  6.   
  7.     private Flee flee;  
  8.   
  9.     // Use this for initialization  
  10.     void Start()  
  11.     {  
  12.         flee = GetComponent<Flee>();  
  13.     }  
  14.   
  15.     public Vector3 getSteering(Rigidbody target)  
  16.     {  
  17.         /* 计算到目标的距离 */  
  18.         Vector3 displacement = target.position - transform.position;  
  19.         float distance = displacement.magnitude;  
  20.   
  21.         /* 获得目标现在的速度 */  
  22.         float speed = target.velocity.magnitude;  
  23.   
  24.         // 计算预测时间 (不能让预测时间能跑的距离 超过当前的距离)  
  25.         float prediction;  
  26.         if (speed <= distance / maxPrediction)  
  27.         {  
  28.             prediction = maxPrediction;  
  29.         }  
  30.         else  
  31.         {  
  32.             prediction = distance / speed;  
  33.             // 目标到达角色前,将预测位置在往前一点  
  34.             prediction *= 0.9f;  
  35.         }  
  36.   
  37.         // 目标 : 在目标位置基础上添加预测的部分  
  38.         Vector3 explicitTarget = target.position   target.velocity * prediction;  
  39.   
  40.         // 使用之前的逃离功能  
  41.         return flee.getSteering(explicitTarget);  
  42.     }  
  43. }  


8 Interpose 干预;插入;调停



这个对象会想办法 走到两个 Wander 漫游对象中间 。

  1. public class InterposeUnit2 : MonoBehaviour {  
  2.   
  3.     public Rigidbody target1;  
  4.   
  5.     public Rigidbody target2;  
  6.   
  7.     private SteeringBasics2 steeringBasics;  
  8.   
  9.     private void Start()  
  10.     {  
  11.         steeringBasics = GetComponent<SteeringBasics2>();  
  12.     }  
  13.   
  14.     private void Update()  
  15.     {  
  16.         Vector3 accel = steeringBasics.Interpose(target1, target2);  
  17.   
  18.         steeringBasics.Steer(accel);  
  19.   
  20.         steeringBasics.LookWhereYoureGoing();  
  21.     }  
  22. }  

SteeringBasics2 .cs 脚本中继续添加:
  1. public Vector3 Interpose(Rigidbody target1, Rigidbody target2)  
  2.     {  
  3.         Vector3 midPoint = (target1.position   target2.position) / 2;  
  4.   
  5.         // 到达 他俩中间需要的时间  
  6.         float timeToReachMidPoint = Vector3.Distance(midPoint, transform.position)/ maxVelocity;  
  7.   
  8.         // 预测 当我到达后他们的位置  
  9.         Vector3 futureTarget1Pos = target1.position   target1.velocity * timeToReachMidPoint;  
  10.         Vector3 futureTarget2Pos = target2.position   target2.velocity * timeToReachMidPoint;  
  11.   
  12.         midPoint = (futureTarget1Pos   futureTarget2Pos) / 2;  
  13.   
  14.         return Arrive(midPoint);  
  15.     }  


9 WallAvoidance 就是躲避墙


提供了两个测试的 场景


  1. public class WallAvoidanceUnit2 : MonoBehaviour  
  2. {  
  3.   
  4.     // 编辑器下设置 路径   
  5.     public LinePath path;  
  6.   
  7.     // 组件  
  8.     private SteeringBasics2 steeringBasics2;  
  9.     private WallAvoidance2 wallAvoidance2;  
  10.     private FollowPath followPath;  
  11.   
  12.     private void Start()  
  13.     {  
  14.         path.calcDistances();  
  15.   
  16.         steeringBasics2 = GetComponent<SteeringBasics2>();  
  17.         wallAvoidance2 = GetComponent<WallAvoidance2>();  
  18.         followPath = GetComponent<FollowPath>();  
  19.     }  
  20.   
  21.     private void Update()  
  22.     {  
  23.         // 到达终点了, 原路返回  
  24.         if (IsAtEndOfPath())  
  25.         {  
  26.             path.reversePath();  
  27.         }  
  28.   
  29.         // 得到加速度 (要躲避墙)  
  30.         Vector3 accel = wallAvoidance2.GetSteering();  
  31.   
  32.         // 沿着路径走了 (约定没有碰撞时, 加速度为0 了)  
  33.         if (accel.magnitude < 0.005f)  
  34.         {  
  35.             accel = followPath.getSteering(path);  
  36.         }  
  37.   
  38.         // 设置刚体 和 朝向  
  39.         steeringBasics2.Steer(accel);  
  40.         steeringBasics2.LookWhereYoureGoing();  
  41.   
  42.         // 调试  
  43.         path.draw();  
  44.     }  
  45.   
  46.     private bool IsAtEndOfPath()  
  47.     {  
  48.         return Vector3.Distance(path.endNode, transform.position) < followPath.stopRadius;  
  49.     }  
  50.   
  51. }  


  1. [RequireComponent(typeof(SteeringBasics2))]  
  2. public class WallAvoidance2 : MonoBehaviour  
  3. {  
  4.   
  5.     /* 前方射线应该延伸多远 */  
  6.     public float mainWhiskerLen = 1.25f;  
  7.   
  8.     /* 跟墙保持的距离 */  
  9.     public float wallAvoidDistance = 0.5f;  
  10.   
  11.     // 两边射线应该延伸多远  
  12.     public float sideWhiskerLen = 0.701f;  
  13.   
  14.     // 两边射线的角度  
  15.     public float sideWhiskerAngle = 45f;  
  16.   
  17.     public float maxAcceleration = 40f;  
  18.   
  19.     // 组件  
  20.     private Rigidbody rb;  
  21.     private SteeringBasics steeringBasics;  
  22.   
  23.     // Use this for initialization  
  24.     void Start()  
  25.     {  
  26.         rb = GetComponent<Rigidbody>();  
  27.         steeringBasics = GetComponent<SteeringBasics>();  
  28.     }  
  29.   
  30.     public Vector3 GetSteering()  
  31.     {  
  32.         return GetSteering(rb.velocity);  
  33.     }  
  34.   
  35.     public Vector3 GetSteering(Vector3 facingDir)  
  36.     {  
  37.         Vector3 acceleration = Vector3.zero;  
  38.   
  39.         /* 创建射线方向向量 */  
  40.         Vector3[] rayDirs = new Vector3[3];  
  41.         // 自己前进的方向  
  42.         rayDirs[0] = facingDir.normalized;  
  43.   
  44.         // 返回弧度, 对边y/临边x   
  45.         float orientation = Mathf.Atan2(rb.velocity.y, rb.velocity.x);  
  46.   
  47.         // 两边的射线方向  
  48.         rayDirs[1] = OrientationToVector(orientation   sideWhiskerAngle * Mathf.Deg2Rad);  
  49.         rayDirs[2] = OrientationToVector(orientation - sideWhiskerAngle * Mathf.Deg2Rad);  
  50.   
  51.         RaycastHit hit;  
  52.   
  53.         /* 如果没有碰撞,什么也不做 */  
  54.         if (!FindObstacle(rayDirs, out hit))  
  55.         {  
  56.             return acceleration;  
  57.         }  
  58.   
  59.         /* 从墙上创建一个目标来 seek (这个方向和射线方向相反)*/  
  60.         Vector3 targetPostition = hit.point   hit.normal * wallAvoidDistance;  
  61.   
  62.         /* 如果速度和碰撞法线平行,则将目标向左或向右移动一点 (如果不矫正就会一直 垂直撞一个地方)*/  
  63.         Vector3 cross = Vector3.Cross(rb.velocity, hit.normal); // 叉乘 判断两个向量是否平行  
  64.         // 点乘“·”计算得到的结果是一个标量; 平行向量 normalized的点乘 是 -1 或者 1, 垂直是0  
  65.         // 叉乘“×”得到的结果是一个垂直于原向量构成平面的向量。 平行向量的叉乘是零向量  
  66.         if (cross.magnitude < 0.005f)  
  67.         {  
  68.             targetPostition = targetPostition   new Vector3(-hit.normal.y, hit.normal.x, hit.normal.z);  
  69.         }  
  70.   
  71.         // 返回最大加速度  
  72.         return steeringBasics.seek(targetPostition, maxAcceleration);  
  73.     }  
  74.   
  75.     /* 将弧度 作为一个 单位向量返回 (极坐标公式)*/  
  76.     private Vector3 OrientationToVector(float orientation)  
  77.     {  
  78.         return new Vector3(Mathf.Cos(orientation), Mathf.Sin(orientation), 0);  
  79.     }  
  80.   
  81.     /// <summary>  
  82.     /// 多个射线, 检测是否发生碰撞  
  83.     /// </summary>  
  84.     /// <param name="rayDirs"></param>  
  85.     /// <param name="firstHit"></param>  
  86.     /// <returns></returns>  
  87.     private bool FindObstacle(Vector3[] rayDirs, out RaycastHit firstHit)  
  88.     {  
  89.         firstHit = new RaycastHit();  
  90.         bool foundObs = false;  
  91.   
  92.         for (int i = 0; i < rayDirs.Length; i )  
  93.         {  
  94.             float rayDist = (i == 0) ? mainWhiskerLen : sideWhiskerLen;  
  95.   
  96.             RaycastHit hit;  
  97.   
  98.             if (Physics.Raycast(transform.position, rayDirs[i], out hit, rayDist))  
  99.             {  
  100.                 foundObs = true;  
  101.                 firstHit = hit;  
  102.                 break;  
  103.             }  
  104.   
  105.             // 调试  
  106.             Debug.DrawLine(transform.position, transform.position   rayDirs[i] * rayDist);  
  107.         }  
  108.   
  109.         return foundObs;  
  110.     }  
  111. }  


10 OffsetPursuit 保持一定偏移的追逐


这个有点意思, 有些场景会用到, 那些白球可以以固定(每个白球相对目标都有设置固定值)的阵型 跟随红球。

Play 场景,那些白球 就会自动 调整站位 跟着红球移动!

每个白球都有一个子对象,上面的脚本是 : NearSensor .cs
  1. /// <summary>  
  2. /// 接近传感器, 保存正在和我接触的,就是发生碰撞的  
  3. /// </summary>  
  4. public class NearSensor : MonoBehaviour {  
  5.   
  6. public HashSet<Rigidbody> targets = new HashSet<Rigidbody>();  
  7.   
  8. void OnTriggerEnter(Collider other) {  
  9. targets.Add (other.GetComponent<Rigidbody>());  
  10. }  
  11.   
  12. void OnTriggerExit(Collider other) {  
  13. targets.Remove (other.GetComponent<Rigidbody>());  
  14. }  
  15. }  

加速度会分为两方面, 成员之间 要分离(保持距离), 还要对主角保持偏移跟随。
组员之间分割脚本的处理如下:
  1. public class Separation : MonoBehaviour {  
  2.   
  3. /* 分隔 加速度 */  
  4. public float sepMaxAcceleration = 25;  
  5.   
  6. /*  
  7. 这应该是分离目标和自身之间可能的最大分离距离。 所以它应该是:分离传感器半径 最大目标半径(separation sensor radius   max target radius ) 
  8. */  
  9. public float maxSepDist = 1f;  
  10.   
  11. private float boundingRadius;  
  12.   
  13. // Use this for initialization  
  14. void Start()  
  15. {  
  16. boundingRadius = SteeringBasics.getBoundingRadius(transform);  
  17. }  
  18.   
  19. public Vector3 getSteering(ICollection<Rigidbody> targets)  
  20. {  
  21. Vector3 acceleration = Vector3.zero;  
  22.   
  23. // 只要存在发生碰撞的就要想办法分离  
  24. foreach (Rigidbody r in targets)  
  25. {  
  26. /* 远离的方向 */  
  27. Vector3 direction = transform.position - r.position;  
  28. float dist = direction.magnitude;  
  29.   
  30. if (dist < maxSepDist)  
  31. {  
  32. float targetRadius = SteeringBasics.getBoundingRadius(r.transform);  
  33.   
  34. /* 计算分离强度(可改为使用平方反比,而不是线性) */  
  35. var strength = sepMaxAcceleration * (maxSepDist - dist) / (maxSepDist - boundingRadius - targetRadius);  
  36.   
  37. /* 将分离加速度增加到现有的 Steer 上 (因为可能不是跟一个发生碰撞) */  
  38. direction.Normalize();  
  39. acceleration  = direction * strength;  
  40. }  
  41. }  
  42.   
  43. return acceleration;  
  44. }  
  45. }  
这个AI 最终使用的脚本!
  1. public class OffsetPursuitUnit : MonoBehaviour {  
  2.   
  3.     public Rigidbody target;  
  4.     public Vector3 offset;  
  5.     public float groupLookDist = 1.5f;  
  6.   
  7.     // 组件  
  8.     private SteeringBasics steeringBasics;  
  9.     private OffsetPursuit offsetPursuit;  
  10.     private Separation separation;  
  11.   
  12.     private NearSensor sensor;  
  13.   
  14.     void Start()  
  15.     {  
  16.         steeringBasics = GetComponent<SteeringBasics>();  
  17.         offsetPursuit = GetComponent<OffsetPursuit>();  
  18.         separation = GetComponent<Separation>();  
  19.   
  20.         sensor = transform.Find("SeparationSensor").GetComponent<NearSensor>();  
  21.     }  
  22.   
  23.     void LateUpdate()  
  24.     {  
  25.         Vector3 targetPos;  
  26.         // 偏移追随加速度 和 分隔加速度  
  27.         Vector3 offsetAccel = offsetPursuit.getSteering(target, offset, out targetPos);  
  28.         Vector3 sepAccel = separation.getSteering(sensor.targets);  
  29.   
  30.         // 速度会 受到两个方面的影响  
  31.         steeringBasics.steer(offsetAccel   sepAccel);  
  32.   
  33.         /* 如果我们还在前往,那就要朝向我们要去的地方,其他的方向和我们的形成目标是一样的 */  
  34.         if (Vector3.Distance(transform.position, targetPos) > groupLookDist)  
  35.         {  
  36.             steeringBasics.lookWhereYoureGoing();  
  37.         } else  
  38.         {  
  39.             steeringBasics.lookAtDirection(target.rotation);  
  40.         }  
  41.     }  
  42. }  

处理 偏移追逐的 加速度
  1. [RequireComponent(typeof(SteeringBasics))]  
  2. public class OffsetPursuit : MonoBehaviour {  
  3. /*未来预测的最大预测时间*/  
  4. public float maxPrediction = 1f;  
  5.   
  6. private Rigidbody rb;  
  7. private SteeringBasics steeringBasics;  
  8.   
  9. // Use this for initialization  
  10. void Start()  
  11. {  
  12. rb = GetComponent<Rigidbody>();  
  13. steeringBasics = GetComponent<SteeringBasics>();  
  14. }  
  15.   
  16. public Vector3 getSteering(Rigidbody target, Vector3 offset)  
  17. {  
  18. Vector3 targetPos;  
  19. return getSteering(target, offset, out targetPos);  
  20. }  
  21.   
  22. public Vector3 getSteering(Rigidbody target, Vector3 offset, out Vector3 targetPos)  
  23. {  
  24. // 得到世界坐标的偏移位置  
  25. Vector3 worldOffsetPos = target.position   target.transform.TransformDirection(offset);  
  26.   
  27. Debug.DrawLine(transform.position, worldOffsetPos);  
  28.   
  29. /* 计算距离到偏移点 */  
  30. Vector3 displacement = worldOffsetPos - transform.position;  
  31. float distance = displacement.magnitude;  
  32.   
  33.   
  34. float speed = rb.velocity.magnitude;  
  35.   
  36. /* 预测的距离不要超过当前距离 */  
  37. float prediction;  
  38. if (speed <= distance / maxPrediction)  
  39. {  
  40. prediction = maxPrediction;  
  41. }  
  42. else  
  43. {  
  44. prediction = distance / speed;  
  45. }  
  46.   
  47. /* 目标位置 */  
  48. targetPos = worldOffsetPos   target.velocity * prediction;  
  49.   
  50. return steeringBasics.arrive(targetPos);  
  51. }  
  52. }  

11 Collision Avoidance 碰撞避免,冲突避免


之前有避免撞墙WallAvoidance, 现在是: CollisionAvoidance .cs
避免撞墙的处理 当时是通过3条射线检测 避免的! 两者还有一个区别, 墙是不动的, 但是这个新的方式是处理 两个都是运动的要怎么避免撞到一起!
那这个是怎么避免? 通过碰撞体 检测,如果有这个确实发生就处理一下就处理一下。


这些对象下面的 ColAvoidSensor 就是起到这个纪录用的(纪录当前与我发生碰撞的对象), 之前提到过。 NearSensor .cs

为了测试让他们运动轨迹是线段, 可以原路返回的 循环。 并且互相交错!
  1. public class ColAvoidUnit : MonoBehaviour {  
  2.     // 编辑器设置  
  3.     public LinePath path;  
  4.   
  5.     // 组件  
  6.     private SteeringBasics steeringBasics;  
  7.     private FollowPath followPath;  
  8.     private CollisionAvoidance colAvoid;  
  9.   
  10.     private NearSensor colAvoidSensor;  
  11.   
  12.     void Start()  
  13.     {  
  14.         path.calcDistances();  
  15.   
  16.         steeringBasics = GetComponent<SteeringBasics>();  
  17.         followPath = GetComponent<FollowPath>();  
  18.         colAvoid = GetComponent<CollisionAvoidance>();  
  19.   
  20.         colAvoidSensor = transform.Find("ColAvoidSensor").GetComponent<NearSensor>();  
  21.     }  
  22.   
  23.     void Update()  
  24.     {  
  25.         // 调试  
  26.         path.draw();  
  27.   
  28.         // 是否原路返回  
  29.         if (isAtEndOfPath())  
  30.         {  
  31.             path.reversePath();  
  32.         }  
  33.   
  34.         // 躲避 加速度  
  35.         Vector3 accel = colAvoid.GetSteering(colAvoidSensor.targets);  
  36.   
  37.         // 不需要躲避 就沿着路径走  
  38.         if (accel.magnitude < 0.005f)  
  39.         {  
  40.             accel = followPath.getSteering(path);  
  41.         }  
  42.   
  43.         // 设置刚体速度 和 朝向  
  44.         steeringBasics.Steer(accel);  
  45.         steeringBasics.LookWhereYoureGoing();  
  46.     }  
  47.   
  48.     public bool isAtEndOfPath()  
  49.     {  
  50.         return Vector3.Distance(path.endNode, transform.position) < followPath.stopRadius;  
  51.     }  
  52. }  


  1. public class CollisionAvoidance : MonoBehaviour  
  2. {  
  3.     public float maxAcceleration = 15f;  
  4.     // 角色半径  
  5.     private float characterRadius;  
  6.   
  7.     private Rigidbody rb;  
  8.   
  9.   
  10.     void Start()  
  11.     {  
  12.         characterRadius = SteeringBasics.GetBoundingRadius(transform);  
  13.   
  14.         rb = GetComponent<Rigidbody>();  
  15.     }  
  16.   
  17.     public Vector3 GetSteering(ICollection<Rigidbody> targets)  
  18.     {  
  19.         Vector3 acceleration = Vector3.zero;  
  20.   
  21.         /* 1. 找出这个角色将会与之碰撞的第一个目标 */  
  22.   
  23.         /* 第一次碰撞时间 */  
  24.         float shortestTime = float.PositiveInfinity; // 正无穷大 临时值  
  25.   
  26.        /* The first target that will collide and other data that 
  27.         * we will need and can avoid recalculating */  
  28.   
  29.        // 重置数据 ,并且可以避免重新计算  
  30.        Rigidbody firstTarget = null;  
  31.         //float firstMinSeparation = 0, firstDistance = 0;  
  32.         float firstMinSeparation = 0,   
  33.             firstDistance = 0,   
  34.             firstRadius = 0;  
  35.         Vector3 firstRelativePos = Vector3.zero,   
  36.             firstRelativeVel = Vector3.zero;  
  37.   
  38.         foreach (Rigidbody r in targets)  
  39.         {  
  40.             /* 计算碰撞时间 */  
  41.             // 相差位置  
  42.             Vector3 relativePos = transform.position - r.position;  
  43.             // 相差速度  
  44.             Vector3 relativeVel = rb.velocity - r.velocity;  
  45.             // 标量  
  46.             float distance = relativePos.magnitude;  
  47.             float relativeSpeed = relativeVel.magnitude;  
  48.   
  49.             // 说明朝着相反的方向运动 并且速度一样  
  50.             if (relativeSpeed == 0)  
  51.             {  
  52.                 continue;  
  53.             }  
  54.   
  55.             //   
  56.             float timeToCollision = -1 * Vector3.Dot(relativePos, relativeVel) / (relativeSpeed * relativeSpeed);  
  57.   
  58.             /* 检查它们是否会碰撞 */  
  59.             Vector3 separation = relativePos   relativeVel * timeToCollision;  
  60.             float minSeparation = separation.magnitude;  
  61.   
  62.             float targetRadius = SteeringBasics.GetBoundingRadius(r.transform);  
  63.   
  64.             // 两者分离了  
  65.             if (minSeparation > characterRadius   targetRadius)  
  66.             //if (minSeparation > 2 * agentRadius)  
  67.             {  
  68.                 continue;  
  69.             }  
  70.   
  71.             /* 检查它是否是最短, 是的话就纪录最短的 */  
  72.             if (timeToCollision > 0 && timeToCollision < shortestTime)  
  73.             {  
  74.                 shortestTime = timeToCollision;  
  75.                 firstTarget = r;  
  76.                 firstMinSeparation = minSeparation;  
  77.                 firstDistance = distance;  
  78.                 firstRelativePos = relativePos;  
  79.                 firstRelativeVel = relativeVel;  
  80.                 firstRadius = targetRadius;  
  81.             }  
  82.         }  
  83.   
  84.         /* 2. 计算加速度 */  
  85.   
  86.         /* 如果没有目标,就退出 */  
  87.         if (firstTarget == null)  
  88.         {  
  89.             return acceleration;  
  90.         }  
  91.   
  92.         /* If we are going to collide with no separation or if we are already colliding then  
  93.    * Steer based on current position */  
  94.         // 如果我们要在没有分离的情况下发生碰撞,或者如果我们已经碰撞了,然后根据当前位置进行碰撞  
  95.         if (firstMinSeparation <= 0 || firstDistance < characterRadius   firstRadius)  
  96.         //if (firstMinSeparation <= 0 || firstDistance < 2 * agentRadius)  
  97.         {  
  98.             acceleration = transform.position - firstTarget.position;  
  99.         }  
  100.         /* 计算未来的相对位置 */  
  101.         else  
  102.         {  
  103.             acceleration = firstRelativePos   firstRelativeVel * shortestTime;  
  104.         }  
  105.   
  106.         /* 远离目标 */  
  107.         acceleration.Normalize();  
  108.         acceleration *= maxAcceleration;  
  109.   
  110.         return acceleration;  
  111.     }  
  112. }  


12 Hide 躲藏


找到障碍然后躲起来。 自己和目标之前有障碍物

第一个脚本: WanderAvoidUnit .cs , CollisionAvoidance .cs 之前说过 Wander 漫步。 这个是在它基础上的升级, 漫步的过程中避免碰撞。 会检测我前进的方向上 出现障碍,那我就换一个方向

  1. public class WanderAvoidUnit : MonoBehaviour {  
  2.   
  3.     private SteeringBasics steeringBasics;  
  4.     private Wander2 wander;  
  5.     private CollisionAvoidance colAvoid;  
  6.   
  7.     // 用于纪录当前都有谁与我发生碰撞  
  8.     private NearSensor colAvoidSensor;  
  9.   
  10.     void Start()  
  11.     {  
  12.         steeringBasics = GetComponent<SteeringBasics>();  
  13.         wander = GetComponent<Wander2>();  
  14.         colAvoid = GetComponent<CollisionAvoidance>();  
  15.   
  16.         colAvoidSensor = transform.Find("ColAvoidSensor").GetComponent<NearSensor>();  
  17.     }  
  18.   
  19.     // Update is called once per frame  
  20.     void Update()  
  21.     {  
  22.         // 加速度(有碰撞 就避免碰撞)  
  23.         Vector3 accel = colAvoid.GetSteering(colAvoidSensor.targets);  
  24.   
  25.         // 没有任何碰撞的时候就 漫游  
  26.         if (accel.magnitude < 0.005f)  
  27.         {  
  28.             accel = wander.getSteering();  
  29.         }  
  30.   
  31.         // 速度  
  32.         steeringBasics.steer(accel);  
  33.         // 朝向  
  34.         steeringBasics.lookWhereYoureGoing();  
  35.     }  
  36. }  

然后就是躲猫猫 脚本 HideUnit .cs Hide .cs
还有躲猫猫实际上是一种 Evade 逃避,逃跑行为!
copy
  1. public class HideUnit : MonoBehaviour {  
  2.     public Rigidbody target;  
  3.   
  4.     private SteeringBasics steeringBasics;  
  5.     private Hide hide;  
  6.     private Spawner obstacleSpawner;  
  7.   
  8.     private WallAvoidance wallAvoid;  
  9.   
  10.     void Start()  
  11.     {  
  12.         steeringBasics = GetComponent<SteeringBasics>();  
  13.         hide = GetComponent<Hide>();  
  14.         obstacleSpawner = GameObject.Find("ObstacleSpawner").GetComponent<Spawner>();  
  15.   
  16.         wallAvoid = GetComponent<WallAvoidance>();  
  17.     }  
  18.   
  19.     void Update()  
  20.     {  
  21.         // 得到加速度 躲在障碍物后面,同时不被目标看到  
  22.         Vector3 hidePosition;  
  23.         Vector3 hideAccel = hide.GetSteering(target, obstacleSpawner.objs, out hidePosition);  
  24.   
  25.         // 如果撞墙要 解决  
  26.         Vector3 accel = wallAvoid.GetSteering(hidePosition - transform.position);  
  27.   
  28.         // 没有撞墙 (说明如果撞墙先解决撞墙)  
  29.         if (accel.magnitude < 0.005f)  
  30.         {  
  31.             accel = hideAccel;  
  32.         }  
  33.   
  34.         // 设置速度 和 朝向  
  35.         steeringBasics.Steer(accel);  
  36.         steeringBasics.LookWhereYoureGoing();  
  37.     }  
  38. }  


  1. [RequireComponent(typeof(SteeringBasics))]  
  2. [RequireComponent(typeof(Evade))]  
  3. public class Hide : MonoBehaviour {  
  4.     // 边界距离 (距离障碍物 边的距离)  
  5.     public float distanceFromBoundary = 0.6f;  
  6.   
  7.     // 组件  
  8.     private SteeringBasics steeringBasics;  
  9.     private Evade evade;  
  10.   
  11.     // Use this for initialization  
  12.     void Start () {  
  13.         steeringBasics = GetComponent<SteeringBasics>();  
  14.         evade = GetComponent<Evade>();  
  15.  }  
  16.   
  17.     public Vector3 GetSteering(Rigidbody target, ICollection<Rigidbody> obstacles, out Vector3 bestHidingSpot)  
  18.     {  
  19.         // 临时值  
  20.         float distToClostest = Mathf.Infinity;  
  21.         bestHidingSpot = Vector3.zero;  
  22.   
  23.         // 找到最近的隐藏点 , 遍历所有障碍  
  24.         foreach(Rigidbody r in obstacles)  
  25.         {  
  26.             // 这个障碍 可以作为隐蔽的位置  
  27.             Vector3 hidingSpot = GetHidingPosition(r, target);  
  28.   
  29.             // 离我多远  
  30.             float dist = Vector3.Distance(hidingSpot, transform.position);  
  31.   
  32.             // 最近就保存  
  33.             if(dist < distToClostest)  
  34.             {  
  35.                 distToClostest = dist;  
  36.                 bestHidingSpot = hidingSpot;  
  37.             }  
  38.         }  
  39.   
  40.         //如果没有发现隐藏点,就只躲避敌人 (比如我和敌人都处于所有障碍的一侧)  
  41.         if (distToClostest == Mathf.Infinity)  
  42.         {  
  43.             return evade.GetSteering(target);  
  44.         }  
  45.   
  46.         Debug.DrawLine(transform.position, bestHidingSpot);  
  47.   
  48.         // 返回加速度  
  49.         return steeringBasics.Arrive(bestHidingSpot);  
  50.     }  
  51.   
  52.     // 获取隐藏位置  
  53.     private Vector3 GetHidingPosition(Rigidbody obstacle, Rigidbody target)  
  54.     {  
  55.         // 这个障碍物 附近  
  56.         float distAway = SteeringBasics.GetBoundingRadius(obstacle.transform)   distanceFromBoundary;  
  57.         // 目标看障碍物的方向(就要躲在这个方向上)  
  58.         Vector3 dir = obstacle.position - target.position;  
  59.         dir.Normalize();  
  60.   
  61.         // 最终隐藏位置  
  62.         return obstacle.position   dir * distAway;  
  63.     }  
  64. }  

随机生成障碍的脚本:
  1. public class Spawner : MonoBehaviour {  
  2.     // Prefab  
  3.     public Transform obj;  
  4.     // 用于随机的范围  
  5.     public Vector2 objectSizeRange = new Vector2(1, 2);  
  6.     // 一次性创建多少个  
  7.     public int numberOfObjects = 10;  
  8.     // 是否随机方向  
  9.     public bool randomizeOrientation = false;  
  10.     // 生成的内容要在 屏幕边界内  
  11.     public float boundaryPadding = 1f;  
  12.     // 生成的内容 距离现有的对象 要保持的最小距离  
  13.     public float spaceBetweenObjects = 1f;  
  14.     public Transform[] thingsToAvoid;  
  15.   
  16.     private Vector3 bottomLeft;  
  17.     private Vector3 widthHeight;  
  18.   
  19.   
  20.     private float[] thingsToAvoidRadius;  
  21.   
  22.     // 纪录现有生成的对象  
  23.     [System.NonSerialized]  
  24.     public List<Rigidbody> objs = new List<Rigidbody>();  
  25.   
  26.     void Start()  
  27.     {  
  28.         // 得到屏幕大小  
  29.         float z = -1 * Camera.main.transform.position.z;  
  30.   
  31.         bottomLeft = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, z));  
  32.         Vector3 topRight = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, z));  
  33.         widthHeight = topRight - bottomLeft;  
  34.   
  35.         // 如果需要的话 要避免和场景内其他对象重叠 纪录他们的数据  
  36.         thingsToAvoidRadius = new float[thingsToAvoid.Length];  
  37.   
  38.         for (int i = 0; i < thingsToAvoid.Length; i )  
  39.         {  
  40.             thingsToAvoidRadius[i] = SteeringBasics.GetBoundingRadius(thingsToAvoid[i].transform);  
  41.         }  
  42.   
  43.         // 创建就行了  
  44.         for (int i = 0; i < numberOfObjects; i )  
  45.         {  
  46.             // 每次不一定创建成功,这里增加概率  
  47.             for(int j = 0; j < 10; j )  
  48.             {  
  49.                 if(TryToCreateObject())  
  50.                 {  
  51.                     break;  
  52.                 }  
  53.             }  
  54.         }  
  55.     }  
  56.   
  57.     /// <summary>  
  58.     /// 尝试创建对象  
  59.     /// </summary>  
  60.     /// <returns></returns>  
  61.     private bool TryToCreateObject()  
  62.     {  
  63.         // 随机位置 和 大小  
  64.         float size = Random.Range(objectSizeRange.x, objectSizeRange.y);  
  65.         float halfSize = size / 2f;  
  66.   
  67.         Vector3 pos = new Vector3();  
  68.         pos.x = bottomLeft.x   Random.Range(boundaryPadding   halfSize, widthHeight.x - boundaryPadding - halfSize);  
  69.         pos.y = bottomLeft.y   Random.Range(boundaryPadding   halfSize, widthHeight.y - boundaryPadding - halfSize);  
  70.   
  71.         // 这个位置可以方式那就实例化  
  72.         if(CanPlaceObject(halfSize, pos))  
  73.         {  
  74.             Transform t = Instantiate(obj, pos, Quaternion.identity) as Transform;  
  75.             t.localScale = new Vector3(size, size, obj.localScale.z);  
  76.   
  77.             if(randomizeOrientation)  
  78.             {  
  79.                 Vector3 euler = transform.eulerAngles;  
  80.                 euler.z = Random.Range(0f, 360f);  
  81.                 transform.eulerAngles = euler;  
  82.             }  
  83.   
  84.             objs.Add(t.GetComponent<Rigidbody>());  
  85.   
  86.             return true;  
  87.         }  
  88.   
  89.         return false;  
  90.     }  
  91.   
  92.     /// <summary>  
  93.     /// 判断是否可以放置, 主要判断是否重叠  
  94.     /// </summary>  
  95.     /// <param name="halfSize"></param>  
  96.     /// <param name="pos"></param>  
  97.     /// <returns></returns>  
  98.     private bool CanPlaceObject(float halfSize, Vector3 pos)  
  99.     {  
  100.         // 确保它不会与任何东西重叠  
  101.         for (int i = 0; i < thingsToAvoid.Length; i )  
  102.         {  
  103.             float dist = Vector3.Distance(thingsToAvoid[i].position, pos);  
  104.   
  105.             if(dist < halfSize   thingsToAvoidRadius[i])  
  106.             {  
  107.                 return false;  
  108.             }  
  109.         }  
  110.   
  111.         //确保它不会与任何现有对象重叠  
  112.         foreach (Rigidbody o in objs)  
  113.         {  
  114.             float dist = Vector3.Distance(o.position, pos);  
  115.   
  116.             float oRadius = SteeringBasics.GetBoundingRadius(o.transform);  
  117.   
  118.             if (dist < oRadius   spaceBetweenObjects   halfSize)  
  119.             {  
  120.                 return false;  
  121.             }  
  122.         }  
  123.   
  124.         return true;  
  125.     }  
  126. }  


13 Flocking集群


测试场景中有 4个 漫游并且避免碰撞的 对象。 他们属于搅局的, 可以看到他们和集群对象的行为差别。
Play 后会生成 75个对象随机分布在场景内。

集群对象都有什么呢?

集群处理 , 和 速度匹配, 同时要与其他对象保持间隔
  1. public class FlockingUnit : MonoBehaviour  
  2. {  
  3.     // 参数  
  4.     public float cohesionWeight = 1.5f;  
  5.     public float separationWeight = 2f;  
  6.     public float velocityMatchWeight = 1f;  
  7.   
  8.     // 速度  
  9.     private SteeringBasics steeringBasics;  
  10.     private Wander2 wander;  
  11.     private Cohesion cohesion;  
  12.     private Separation separation;  
  13.     private VelocityMatch velocityMatch;  
  14.   
  15.     private NearSensor sensor;  
  16.   
  17.     // Use this for initialization  
  18.     void Start()  
  19.     {  
  20.         steeringBasics = GetComponent<SteeringBasics>();  
  21.         wander = GetComponent<Wander2>();  
  22.         cohesion = GetComponent<Cohesion>();  
  23.         separation = GetComponent<Separation>();  
  24.         velocityMatch = GetComponent<VelocityMatch>();  
  25.   
  26.         sensor = transform.Find("Sensor").GetComponent<NearSensor>();  
  27.     }  
  28.   
  29.     // Update is called once per frame  
  30.     void Update()  
  31.     {  
  32.         Vector3 accel = Vector3.zero;  
  33.         // 集群加速度  
  34.         accel  = cohesion.GetSteering(sensor.targets) * cohesionWeight;  
  35.         // 分隔加速度  
  36.         accel  = separation.GetSteering(sensor.targets) * separationWeight;  
  37.         // 速度匹配加速度  
  38.         accel  = velocityMatch.GetSteering(sensor.targets) * velocityMatchWeight;  
  39.   
  40.         // 如果没有那些 影响, 就漫游好了  
  41.         if (accel.magnitude < 0.005f)  
  42.         {  
  43.             accel = wander.GetSteering();  
  44.         }  
  45.   
  46.         // 设置刚体速度 和 朝向  
  47.         steeringBasics.Steer(accel);  
  48.         steeringBasics.LookWhereYoureGoing();  
  49.     }  
  50. }  

  1. [RequireComponent(typeof(SteeringBasics))]  
  2. public class Cohesion : MonoBehaviour {  
  3.     // 我的前方视野  
  4.     public float facingCosine = 120f;  
  5.   
  6.     private float facingCosineVal;  
  7.   
  8.     private SteeringBasics steeringBasics;  
  9.   
  10.  // Use this for initialization  
  11.  void Start () {  
  12.         facingCosineVal = Mathf.Cos(facingCosine * Mathf.Deg2Rad);  
  13.         steeringBasics = GetComponent<SteeringBasics>();  
  14.  }  
  15.   
  16.     public Vector3 GetSteering(ICollection<Rigidbody> targets)  
  17.     {  
  18.         Vector3 centerOfMass = Vector3.zero;  
  19.         int count = 0;  
  20.   
  21.         /* 得到我前方视野内所有角色的 中心 */  
  22.         foreach (Rigidbody r in targets)  
  23.         {  
  24.             // 在视野内 (视野是 无限扇形)  
  25.             if (steeringBasics.isFacing(r.position, facingCosineVal))  
  26.             {  
  27.                 centerOfMass  = r.position;  
  28.                 count ;  
  29.             }  
  30.         }  
  31.   
  32.         if (count == 0) // 我前面没有人。 漫游好了  
  33.         {  
  34.             return Vector3.zero;  
  35.         }  
  36.         else  
  37.         {  
  38.             // 目标目标位置  
  39.             centerOfMass = centerOfMass / count;  
  40.   
  41.             return steeringBasics.Arrive(centerOfMass);  
  42.         }  
  43.     }  
  44. }  

速度匹配与之类似: copy
  1. [RequireComponent(typeof(SteeringBasics))]  
  2. public class VelocityMatch : MonoBehaviour  
  3. {  
  4.     // 视野范围  
  5.     public float facingCosine = 90;  
  6.   
  7.     public float timeToTarget = 0.1f;  
  8.     public float maxAcceleration = 4f;  
  9.   
  10.     // 视野的余弦值  
  11.     private float facingCosineVal;  
  12.   
  13.   
  14.     private Rigidbody rb;  
  15.     private SteeringBasics steeringBasics;  
  16.   
  17.     // Use this for initialization  
  18.     void Start()  
  19.     {  
  20.         facingCosineVal = Mathf.Cos(facingCosine * Mathf.Deg2Rad);  
  21.   
  22.         rb = GetComponent<Rigidbody>();  
  23.         steeringBasics = GetComponent<SteeringBasics>();  
  24.     }  
  25.   
  26.     public Vector3 GetSteering(ICollection<Rigidbody> targets)  
  27.     {  
  28.         Vector3 accel = Vector3.zero;  
  29.         int count = 0;  
  30.   
  31.         // 得到我视野内 所有人的 加速度平均值  
  32.         foreach (Rigidbody r in targets)  
  33.         {  
  34.             if (steeringBasics.isFacing(r.position, facingCosineVal))  
  35.             {  
  36.                 /* 计算我们想要匹配这个目标的加速度 */  
  37.                 Vector3 a = r.velocity - rb.velocity;  
  38.                 /* 
  39.                  Rather than accelerate the character to the correct speed in 1 second,  
  40.                  accelerate so we reach the desired speed in timeToTarget seconds  
  41.                  (if we were to actually accelerate for the full timeToTarget seconds). 
  42.                 */  
  43.                 // 而不是在1秒内加速字符的正确速度,这样我们就能在目标秒内达到所期望的速度(如果我们要在目标秒内加速的话)。  
  44.                 a = a / timeToTarget;  
  45.   
  46.                 accel  = a;  
  47.   
  48.                 count ;  
  49.             }  
  50.         }  
  51.   
  52.         if (count > 0)  
  53.         {  
  54.             accel = accel / count;  
  55.   
  56.             /* 不要超值 */  
  57.             if (accel.magnitude > maxAcceleration)  
  58.             {  
  59.                 accel = accel.normalized * maxAcceleration;  
  60.             }  
  61.         }  
  62.   
  63.         return accel;  
  64.     }  
  65. }  


http://blog.csdn.net/u010019717/article/details/78079446

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引