| using UnityEngine; | 
| using System.Collections.Generic; | 
|   | 
| public class CollisionUtility | 
| { | 
|   | 
|     /// <summary> | 
|     /// 扫描碰撞检测 | 
|     /// </summary> | 
|     /// <param name="_start_pos">扫描的起点</param> | 
|     /// <param name="_end_pos">扫描的终点</param> | 
|     /// <param name="_sweep_radius">扫描的半径</param> | 
|     /// <param name="_position">被扫描对象的坐标</param> | 
|     /// <param name="_height">被扫描对象的高度,碰撞体的高度已经包括了半径</param> | 
|     /// <param name="_radius">被扫描对象的半径</param> | 
|     /// <returns>返回是否扫描到</returns> | 
|     public static bool ScanCollide(Vector3 _start_pos, Vector3 _end_pos, float _sweep_radius, | 
|                                     Vector3 _position, float _height, float _radius) | 
|     { | 
|         // 转为判断2个向量的距离判断 | 
|         // 因为要扫的对象都是不会左右旋转的,是一个直立的胶囊提对象,所以取得最高点和最低点 | 
|         // 与扫的起点和终点为2个向量 | 
|         float low_x_1 = 0, height_x_1 = 0, low_y_1 = 0, height_y_1 = 0, low_z_1 = 0, height_z_1 = 0; | 
|         // 计算2个向量的高点和低点 | 
|         if (_start_pos.y > _end_pos.y) | 
|         { | 
|             low_y_1 = _end_pos.y; | 
|             height_y_1 = _start_pos.y; | 
|         } | 
|         else | 
|         { | 
|             low_y_1 = _start_pos.y; | 
|             height_y_1 = _end_pos.y; | 
|         } | 
|   | 
|         if (_start_pos.x > _end_pos.x) | 
|         { | 
|             low_x_1 = _end_pos.x; | 
|             height_x_1 = _start_pos.x; | 
|         } | 
|         else | 
|         { | 
|             low_x_1 = _start_pos.x; | 
|             height_x_1 = _end_pos.x; | 
|         } | 
|   | 
|         if (_start_pos.z > _end_pos.z) | 
|         { | 
|             low_z_1 = _end_pos.z; | 
|             height_z_1 = _start_pos.z; | 
|         } | 
|         else | 
|         { | 
|             low_z_1 = _start_pos.z; | 
|             height_z_1 = _end_pos.z; | 
|         } | 
|   | 
|         float low_y_2 = _position.y; | 
|         float height_y_2 = _position.y + _height; | 
|   | 
|         // 如果其中一个的高点比另一个的低点-半径还要低,或者低点比另一个的高点+半径还要高,则肯定不会碰撞 | 
|         if (low_y_1 - _sweep_radius > height_y_2 || height_y_1 + _sweep_radius < low_y_2 || | 
|             low_x_1 - _sweep_radius > _position.x + _radius || height_x_1 + _sweep_radius < _position.x - _radius || | 
|             low_z_1 - _sweep_radius > _position.z + _radius || height_z_1 + _sweep_radius < _position.z - _radius) | 
|             return false; | 
|   | 
|         // 再来就是比较2个向量的距离,如果距离小于2个半径,则会碰撞 | 
|         Vector3 vec1 = _end_pos - _start_pos; | 
|         Vector3 vec2 = new Vector3(0, _height, 0); | 
|   | 
|         float d = float.MaxValue; | 
|   | 
|         // 这里要判断是否平行的情况 | 
|         // 只需要判断vec1的x和z是否为0,为0则与要判断向量平行 | 
|         if (vec1.x == 0 && vec1.z == 0) | 
|         { | 
|             d = Mathf.Sqrt((_start_pos.x - _position.x) * (_start_pos.x - _position.x) + (_start_pos.z - _position.z) * (_start_pos.z - _position.z)); | 
|         } | 
|         else | 
|         { | 
|             // 扫描向量上任取一点A,被扫对象向量上任选一点B组成新的向量AB, | 
|             // 求出扫描向量和被扫向量的法向量N | 
|             // 则这2个向量的距离为 d = AB * N / N的模 | 
|             Vector3 N = Vector3.Cross(vec1, vec2); | 
|             Vector3 AB = _start_pos - _position; | 
|   | 
|             d = Mathf.Abs(N.x * AB.x + N.y * AB.y + N.z * AB.z) / Mathf.Sqrt(N.x * N.x + N.y * N.y + N.z * N.z); | 
|         } | 
|   | 
|         // CatDebugger.Log("distance : " + d + " , range : " + (_sweep_radius + _radius)); | 
|   | 
|         return d < _sweep_radius + _radius; | 
|     } | 
|   | 
|   | 
|     public static bool Sweep(List<Vector3> points, | 
|                              float radius, | 
|                              Vector3 targetPosition, | 
|                              float targetRadius) | 
|     { | 
|   | 
|         bool _result = false; | 
|   | 
|         if (points.Count == 1) | 
|         { | 
|             return Vector3.SqrMagnitude(targetPosition - points[0]) < Mathf.Pow(radius + targetRadius, 2); | 
|         } | 
|   | 
|         Vector3 _from; | 
|         Vector3 _to; | 
|   | 
|         for (int i = 0; i < points.Count - 1; ++i) | 
|         { | 
|             _from = points[i]; | 
|             _to = points[i + 1]; | 
|             if (CircleCollied(_from, _to, radius, targetPosition, targetRadius)) | 
|             { | 
|                 _result = true; | 
|                 break; | 
|             } | 
|         } | 
|   | 
|         return _result; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 一段距离的线段的平面圆扫描检测 | 
|     /// </summary> | 
|     /// <param name="f">线段起点</param> | 
|     /// <param name="t">线段终点</param> | 
|     /// <param name="radius">圆的半径</param> | 
|     /// <param name="p">检测点</param> | 
|     /// <param name="targetRadius">检测半径</param> | 
|     /// <returns>返回距离是否小于2个半径的长度</returns> | 
|     public static bool CircleCollied(Vector3 f, | 
|                                      Vector3 t, | 
|                                      float radius, | 
|                                      Vector3 p, | 
|                                      float targetRadius) | 
|     { | 
|   | 
|         // 对y进行归零 | 
|         f.y = 0; | 
|         t.y = 0; | 
|         p.y = 0; | 
|   | 
|         // 得到真实的距离差 | 
|         float _radiusSum = radius + targetRadius; | 
|   | 
|         // 如果传入的起点,终点相同, 则只计算2点之间的距离是否小于半径 | 
|         if (f == t) | 
|         { | 
|             return Vector3.SqrMagnitude(p - f) < _radiusSum * _radiusSum; | 
|         } | 
|   | 
|         // 如果点在两端之外,则一定不会碰撞 | 
|         // 起点x更大 | 
|         if (f.x > t.x) | 
|         { | 
|             if (p.x > f.x) | 
|             { | 
|                 return Vector3.SqrMagnitude(p - f) < _radiusSum * _radiusSum; | 
|             } | 
|         } | 
|         else | 
|         {// 终点更大 | 
|             if (p.x > t.x) | 
|             { | 
|                 return Vector3.SqrMagnitude(p - t) < _radiusSum * _radiusSum; | 
|             } | 
|         } | 
|   | 
|         // 起点z更大 | 
|         if (f.z > t.z) | 
|         { | 
|             if (p.z > f.z) | 
|             { | 
|                 return Vector3.SqrMagnitude(p - f) < _radiusSum * _radiusSum; | 
|             } | 
|         } | 
|         else | 
|         { | 
|             if (p.z > t.z) | 
|             { | 
|                 return Vector3.SqrMagnitude(p - t) < _radiusSum * _radiusSum; | 
|             } | 
|         } | 
|   | 
|         float _distance = 0; | 
|   | 
|         if (f.x == t.x) | 
|         { | 
|             Debug.LogFormat("_distance: {0}", Mathf.Abs(p.x - f.x)); | 
|             // 只要保证 | 
|         } | 
|   | 
|         float cross = (t.x - f.x) * (p.x - f.x) + (t.z - f.z) * (p.z - f.z); | 
|         if (cross <= 0) | 
|         { | 
|             _distance = Mathf.Sqrt((p.x - f.x) * (p.x - f.x) + (p.z - f.z) * (p.z - f.z)); | 
|             return _distance < _radiusSum; | 
|         } | 
|   | 
|         float d2 = (t.x - f.x) * (t.x - f.x) + (t.z - f.z) * (t.z - f.z); | 
|         if (cross >= d2) | 
|         { | 
|             _distance = Mathf.Sqrt((p.x - t.x) * (p.x - t.x) + (p.z - t.z) * (p.z - t.z)); | 
|             return _distance < _radiusSum; | 
|         } | 
|   | 
|         float r = cross / d2; | 
|         float px = f.x + (t.x - f.x) * r; | 
|         float py = f.z + (t.z - f.z) * r; | 
|   | 
|         _distance = Mathf.Sqrt((p.x - px) * (p.x - px) + (py - p.z) * (py - p.z)); | 
|         return _distance < _radiusSum; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 检测点是否在给定的矩形中 | 
|     /// </summary> | 
|     /// <param name="position">矩形位置</param> | 
|     /// <param name="direction">矩形的朝向</param> | 
|     /// <param name="width">矩形的宽度</param> | 
|     /// <param name="distance">矩形的距离</param> | 
|     /// <param name="checkPoint">检测点的坐标</param> | 
|     /// <returns>是否在检测范围内</returns> | 
|     public static bool IsPointInRectangle(Vector3 position, | 
|                                           Vector3 direction, | 
|                                           float width, | 
|                                           float distance, | 
|                                           Vector3 checkPoint) | 
|     { | 
|         Vector3 p1 = position + Quaternion.Euler(0, -90, 0) * direction * width * .5f; | 
|         Vector3 p2 = p1 + direction * distance; | 
|         Vector3 p3 = p2 + Quaternion.Euler(0, 90, 0) * direction * width; | 
|         Vector3 p4 = position + Quaternion.Euler(0, 90, 0) * direction * width * .5f; | 
|   | 
|         Line2D _line1 = new Line2D | 
|         { | 
|             start = new Vector2(p1.x, p1.z), | 
|             end = new Vector2(p2.x, p2.z) | 
|         }; | 
|   | 
|         Line2D _line2 = new Line2D | 
|         { | 
|             start = new Vector2(p2.x, p2.z), | 
|             end = new Vector2(p3.x, p3.z) | 
|         }; | 
|   | 
|         Line2D _line3 = new Line2D | 
|         { | 
|             start = new Vector2(p3.x, p3.z), | 
|             end = new Vector2(p4.x, p4.z) | 
|         }; | 
|   | 
|         Line2D _line4 = new Line2D | 
|         { | 
|             start = new Vector2(p4.x, p4.z), | 
|             end = new Vector2(p1.x, p1.z) | 
|         }; | 
|   | 
|         bool result = Line2D.PointAtLineLeft(new Vector2(checkPoint.x, checkPoint.z), _line1) | 
|                    || Line2D.PointAtLineLeft(new Vector2(checkPoint.x, checkPoint.z), _line2) | 
|                    || Line2D.PointAtLineLeft(new Vector2(checkPoint.x, checkPoint.z), _line3) | 
|                    || Line2D.PointAtLineLeft(new Vector2(checkPoint.x, checkPoint.z), _line4); | 
|   | 
|         return !result; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 检测给定的点是否在给定的扇形区域中 | 
|     /// </summary> | 
|     /// <param name="position">扇形的圆心点</param> | 
|     /// <param name="direction">扇形的朝向</param> | 
|     /// <param name="radius">扇形的半径</param> | 
|     /// <param name="angle">扇形的张角</param> | 
|     /// <param name="checkPoint">检测点坐标</param> | 
|     /// <returns></returns> | 
|     public static bool IsPointInSector(Vector3 position, | 
|                                        Vector3 direction, | 
|                                        float radius, | 
|                                        float angle, | 
|                                        Vector3 checkPoint) | 
|     { | 
|         // 半径检测 | 
|         if (!IsPointsInDistance(position, checkPoint, radius)) | 
|         { | 
|             return false; | 
|         } | 
|   | 
|         // 扇形范围检测 | 
|         Vector3 _chkToSelf = (checkPoint - position).normalized; | 
|         float _chkAngle = Mathf.Acos(Vector3.Dot(direction, _chkToSelf)) * Mathf.Rad2Deg; | 
|         return _chkAngle <= angle * .5f; | 
|     } | 
|   | 
|     public static bool IsPointInSector2(Vector3 position, | 
|                                        Vector3 direction, | 
|                                        float radius, | 
|                                        float angle, | 
|                                        Vector3 checkPoint) | 
|     { | 
|         return IsPointInSector2Sqrt(position, direction, radius * radius, angle, checkPoint); | 
|     } | 
|   | 
|     public static bool IsPointInSector2Sqrt(Vector3 position, | 
|                                    Vector3 direction, | 
|                                    float radiusSqrt, | 
|                                    float angle, | 
|                                    Vector3 checkPoint) | 
|     { | 
|         float _chkDistSqrt = MathUtility.DistanceSqrtXZ(position, checkPoint); | 
|   | 
|         // 半径检测 | 
|         if (_chkDistSqrt > radiusSqrt) | 
|         { | 
|             return false; | 
|         } | 
|   | 
|         Vector3 _targetDir = (checkPoint - position).normalized; | 
|         float _chkAngle = Vector3.Angle(_targetDir, direction); | 
|         bool _result = _chkAngle <= angle * .5f; | 
|         return _result; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 判断2个点的距离是否小于给定距离 | 
|     /// </summary> | 
|     /// <param name="position">检测点</param> | 
|     /// <param name="checkPosition">检测点</param> | 
|     /// <param name="distance">距离</param> | 
|     /// <returns></returns> | 
|     public static bool IsPointsInDistance(Vector3 position, Vector3 checkPosition, float distance) | 
|     { | 
|         return Vector3.SqrMagnitude(checkPosition - position) <= distance * distance; | 
|     } | 
|   | 
|   | 
|     public static bool TryGetGroundHeight(Vector3 _position, out float _height) | 
|     { | 
|         RaycastHit hit; | 
|         var ray = new Ray(_position.SetY(150f), Vector3.down); | 
|   | 
|         if (Physics.Raycast(ray, out hit, 200f, LayerUtility.WalkbleMask)) | 
|         { | 
|             _height = hit.point.y; | 
|             return true; | 
|         } | 
|         else | 
|         { | 
|             _height = 0f; | 
|             return false; | 
|         } | 
|     } | 
|   | 
|     public static bool NM_RayCast(Vector3 sourcePosition, Vector3 targetPosition, ref Vector3 result) | 
|     { | 
|         sourcePosition.y = 0; | 
|         targetPosition.y = 0; | 
|   | 
|         UnityEngine.AI.NavMeshHit _hit; | 
|   | 
|         if (UnityEngine.AI.NavMesh.Raycast(sourcePosition, targetPosition, out _hit, UnityEngine.AI.NavMesh.AllAreas)) | 
|         { | 
|             result = _hit.position; | 
|             return true; | 
|         } | 
|   | 
|         return false; | 
|     } | 
| } |