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;
}
}