using UnityEngine; using System.Collections.Generic; public class CollisionUtility { /// /// 扫描碰撞检测 /// /// 扫描的起点 /// 扫描的终点 /// 扫描的半径 /// 被扫描对象的坐标 /// 被扫描对象的高度,碰撞体的高度已经包括了半径 /// 被扫描对象的半径 /// 返回是否扫描到 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 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; } /// /// 一段距离的线段的平面圆扫描检测 /// /// 线段起点 /// 线段终点 /// 圆的半径 /// 检测点 /// 检测半径 /// 返回距离是否小于2个半径的长度 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; } /// /// 检测点是否在给定的矩形中 /// /// 矩形位置 /// 矩形的朝向 /// 矩形的宽度 /// 矩形的距离 /// 检测点的坐标 /// 是否在检测范围内 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; } /// /// 检测给定的点是否在给定的扇形区域中 /// /// 扇形的圆心点 /// 扇形的朝向 /// 扇形的半径 /// 扇形的张角 /// 检测点坐标 /// 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; } /// /// 判断2个点的距离是否小于给定距离 /// /// 检测点 /// 检测点 /// 距离 /// 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; } }