平面攻击检测

平面攻击检测

/// <summary>
/// 点在直线上
/// 使用斜率计算,先排除 斜率不存在和与另外两点重合
/// </summary>
/// <param name="p"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
public bool IsPointInLine(Vector3 p, Vector3 p1, Vector3 p2)
{
    /// 先排除 p 与 p1,p2 两点相等
    if (p1.x == p.x || p2.x == p.x)
    {
        /// p1.x==p2.x 一条竖线 x 相等在一条直线上
        /// 和线上的点相等
        if (p1.x == p2.x || p.x == p1.x && p.z == p1.z || p.x == p2.x && p.z == p2.z)
            return true;
        return false;
    }
    /// k=(y-y1)/(x-x1) 斜率相等并且有公共交点,一条直线
    if ((p1.z - p.z) / (p1.x - p.x) == (p2.z - p.z) / (p2.x - p.x))
        return true;
    return false;
}

/// <summary>
/// 点在扇形内
/// </summary>
/// <param name="p">检测点</param>
/// <param name="center">扇形圆心</param>
/// <param name="dir">方向</param>
/// <param name="len">长度</param>
/// <param name="angle">角度</param>
/// <returns></returns>
public bool IsPointInSector(Vector3 p, Vector3 center, Vector3 dir, float len, float angle)
{
    p.y = center.y = dir.y = 0;
    /// 距离
    if (Vector3.Distance(p, center) > len)
    {
        return false;
    }
    return IsPointAngleInSector(p, center, dir, angle);
}

/// <summary>
/// 点在圆内
/// </summary>
/// <param name="p"></param>
/// <param name="center"></param>
/// <param name="radius"></param>
/// <returns></returns>
public bool IsPointInCircle(Vector3 p, Vector3 center, float radius)
{
    return Vector3.Distance(p, center) <= radius;
}

/// <summary>
/// 点在矩形内
/// </summary>
/// A_______________B
/// |           dir |
/// |------ 0 ----->|
/// |_______________|
/// D               C
/// <param name="p">点</param>
/// <param name="center">矩形中心</param>
/// <param name="dir">矩形方向,对应长</param>
/// <param name="len">长</param>
/// <param name="width">宽</param>
/// <returns></returns>
public bool IsPointInRect(Vector3 p, Vector3 center, Vector3 dir, float len, float width)
{
    dir = dir.normalized;
    Vector3 dirV = GetVertical(dir);
    Vector3 dirStart = center + len / 2 * dir;
    Vector3 dirEnd = center - len / 2 * dir;
    Vector3 A = dirEnd - width / 2 * dirV;
    Vector3 D = dirEnd + width / 2 * dirV;
    Vector3 B = dirStart - width / 2 * dirV;
    Vector3 C = dirStart + width / 2 * dirV;
    return IsPointInRect(p, A, B, C, D);
}
/// <summary>
/// 点在矩形内
/// </summary>
/// <param name="p"></param>
/// <param name="A"></param>
/// <param name="B"></param>
/// <param name="C"></param>
/// <param name="D"></param>
/// <returns></returns>
public bool IsPointInRect(Vector3 p, Vector3 A, Vector3 B, Vector3 C, Vector3 D)
{
    return GetCross(A, B, p) * GetCross(C, D, p) >= 0 && GetCross(B, C, p) * GetCross(D, A, p) >= 0;
}

/// <summary>
/// 线段ab与线段cd相交
/// c d 两点在 a b 两侧,a b 两点在 c d 两侧
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <param name="d"></param>
/// <returns></returns>
public bool IsLineCrossLine(Vector3 a, Vector3 b, Vector3 c, Vector3 d, bool useMethod = true)
{
    if (useMethod)
    {
        /// 根据范围 a b 线段 x 坐标和 c d 线段没有交集,同 y
        if (Mathf.Max(a.x, b.x) < Mathf.Min(c.x, d.x) || Mathf.Max(c.x, d.x) < Mathf.Min(a.x, b.x)
            || Mathf.Max(a.y, b.y) < Mathf.Min(c.y, d.y) || Mathf.Max(c.y, d.y) < Mathf.Min(a.y, b.y))
        {
            return false;
        }
        /// c d 两点在 a b 两侧,a b 两点在 c d 两侧
        if (GetCross(a, b, c) * GetCross(a, b, d) <= 0 && GetCross(c, d, a) * GetCross(c, d, b) <= 0)
            return true;
        return false;
    }
    else
    {
        int num = LineCrossPoint(a, b, c, d, out Vector3 cross);
        /// 平行无交点
        if (num == 0) return false;
        /// 重合 
        /// 1.c d,有一个或都在 ab 中间 相交
        /// 2.c d,在 ab 两侧   相交
        /// 3.c d,在 ab 一侧   不相交 
        if (num == 2)
        {
            bool cp = GetDot(c, a, b) <= 0;
            bool dp = GetDot(d, a, b) <= 0;
            /// 有一个在中间
            if (cp || dp)
                return true;
            /// 大于0 表示,a 不在 c d 中间
            return GetDot(a, c, d) <= 0;
            ;
        }
        /// 只剩 1 了,有一个交点,交点在 ab 中间,就相交
        return GetDot(cross, a, b) <= 0 && GetDot(cross, c, d) <= 0;
    }

}

/// <summary>
/// 线段与圆相交
/// </summary>
/// <param name="center"></param>
/// <param name="radius"></param>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public bool IsLineCrossCircle(Vector3 center, float radius, Vector3 a, Vector3 b)
{
    return PointLineDistance(center, a, b) <= radius;
}

/// <summary>
/// 线段与矩形相交
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="center"></param>
/// <param name="dir"></param>
/// <param name="len"></param>
/// <param name="width"></param>
/// <returns></returns>
public bool IsLineCrossRect(Vector3 p1, Vector3 p2, Vector3 center, Vector3 dir, float len, float width)
{
    dir = dir.normalized;
    Vector3 dirV = GetVertical(dir);
    Vector3 dirStart = center + len / 2 * dir;
    Vector3 dirEnd = center - len / 2 * dir;
    Vector3 A = dirEnd - width / 2 * dirV;
    Vector3 D = dirEnd + width / 2 * dirV;
    Vector3 B = dirStart - width / 2 * dirV;
    Vector3 C = dirStart + width / 2 * dirV;
    return IsLineCrossRect(p1, p2, A, B, C, D);

}
/// <summary>
/// 线段与矩形相交
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="A"></param>
/// <param name="B"></param>
/// <param name="C"></param>
/// <param name="D"></param>
/// <returns></returns>
public bool IsLineCrossRect(Vector3 p1, Vector3 p2, Vector3 A, Vector3 B, Vector3 C, Vector3 D)
{
    /// 有一点在矩形内
    if (IsPointInRect(p1, A, B, C, D) || IsPointInRect(p2, A, B, C, D))
    {
        return true;
    }
    /// 有一条线段相交
    return IsLineCrossLine(p1, p2, A, B) || IsLineCrossLine(p1, p2, B, C) || IsLineCrossLine(p1, p2, C, D) || IsLineCrossLine(p1, p2, D, A);
}

/// <summary>
/// 圆与扇形相交
/// </summary>
/// <param name="p">圆心</param>
/// <param name="radius">圆半径</param>
/// <param name="center">扇形圆心</param>
/// <param name="dir">方向</param>
/// <param name="len">长度</param>
/// <param name="angle">角度</param>
/// <returns></returns>
/// <returns></returns>
public bool IsCircleCrossSector(Vector3 p, float radius, Vector3 center, Vector3 dir, float len, float angle)
{
    p.y = center.y = dir.y = 0;
    /// 距离
    if (Vector3.Distance(p, center) > radius + len)
    {
        return false;
    }
    /// 角度在扇形里,前面距离也够,所有是有相交点的
    if (IsPointAngleInSector(p, center, dir, angle))
    {
        return true;
    }
    Vector3 left = Quaternion.AngleAxis(-angle / 2, Vector3.up) * dir.normalized * len;
    Vector3 right = Quaternion.AngleAxis(angle / 2, Vector3.up) * dir.normalized * len;
    if (PointLineDistance(p, center, center + left) <= radius || PointLineDistance(p, center, center + right) <= radius)
        return true;
    return false;
}

/// <summary>
/// 圆与圆相交或者包含
/// </summary>
/// <param name="c1"></param>
/// <param name="r1"></param>
/// <param name="c2"></param>
/// <param name="r2"></param>
/// <returns></returns>
public bool IsCircleCrossCircle(Vector3 c1, float r1, Vector3 c2, float r2)
{
    c1.y = c2.y = 0;
    return Vector3.Distance(c1, c2) <= r1 + r2;
}

/// <summary>
/// 圆与矩形相交
/// 第一步 圆心是否在矩形内 在返回 true
/// 第二步 圆心到四条边的距离小于半径,有一条返回true
/// </summary>
/// A_______________B
/// |           dir |
/// |------ 0 ----->|
/// |_______________|
/// D               C
/// <param name="p">圆心</param>
/// <param name="radius"></param>
/// <param name="center"></param>
/// <param name="dir"></param>
/// <param name="len"></param>
/// <param name="width"></param>
/// <returns></returns>
public bool IsCircleCrossRect(Vector3 p, float radius, Vector3 center, Vector3 dir, float len, float width)
{
    dir = dir.normalized;
    Vector3 dirV = GetVertical(dir);
    Vector3 dirStart = center + len / 2 * dir;
    Vector3 dirEnd = center - len / 2 * dir;
    Vector3 A = dirEnd - width / 2 * dirV;
    Vector3 D = dirEnd + width / 2 * dirV;
    Vector3 B = dirStart - width / 2 * dirV;
    Vector3 C = dirStart + width / 2 * dirV;
    /// 第一步 圆心是否在矩形内 在返回 true
    // GetCross(A, B, p) * GetCross(C, D, p) >= 0 说明在AB CD 两条线中间
    // GetCross(B, C, p) * GetCross(D, A, p) >= 0 说明在BC DA 两条线中间
    if (GetCross(A, B, p) * GetCross(C, D, p) >= 0 && GetCross(B, C, p) * GetCross(D, A, p) >= 0)
        return true;
    /// 第二步 圆心到四条边的距离小于半径,有一条返回true
    if (PointLineDistance(p, A, B) <= radius || PointLineDistance(p, B, C) <= radius || PointLineDistance(p, C, D) <= radius || PointLineDistance(p, D, A) <= radius)
        return true;
    return false;
}

/// <summary>
/// 矩形与矩形相交
/// </summary>
/// <param name="center1"></param>
/// <param name="dir1"></param>
/// <param name="len1"></param>
/// <param name="width1"></param>
/// <param name="center2"></param>
/// <param name="dir2"></param>
/// <param name="len2"></param>
/// <param name="width2"></param>
/// <returns></returns>
public bool IsRectCrossRect(Vector3 center1, Vector3 dir1, float len1, float width1, Vector3 center2, Vector3 dir2, float len2, float width2)
{
    return true;
}

/// <summary>
/// 点是否在扇形角度内
/// </summary>
/// <param name="p"></param>
/// <param name="center"></param>
/// <param name="dir"></param>
/// <param name="angle"></param>
/// <returns></returns>
bool IsPointAngleInSector(Vector3 p, Vector3 center, Vector3 dir, float angle)
{
    if (angle >= 360f)
        return true;
    Vector3 target = p - center;
    float LineAngle = Vector3.Angle(target, dir);
    bool inSectorAngle = LineAngle <= angle / 2f;
    return inSectorAngle;
}

/// <summary>
/// 点到线段的距离
/// 第一种:
/// 求点在直线上的投影 c ,
/// 判断投影是否在直线上,在返回点到投影的距离,
/// 不在返回点到两端点距离的小值
/// 第二种:
/// 判断点是否在线段上,在返回最小值
/// 不在,判断角 pp1p2 或者 pp2p1 是否是钝角或者直角 
/// 判断方法 a*a>=c*c+b*b  a是斜边 另外一个角是钝角或直角 点 p 到另外一个点的距离最近
/// 根据三角形面积
/// S=Sqrt(p(p-a)(p-b)(p-c))    p=(a+b+c)/2
/// S=d*h/2 h为点到直线的距离
/// h=2S/d
/// </summary>
/// <returns></returns>
float PointLineDistance(Vector3 p, Vector3 p1, Vector3 p2, bool useMethod = false)
{
    if (p1 == p2)
        return Vector3.Distance(p1, p);
    if (useMethod)
    {
        /// 第一种
        Vector3 cross = PointVerticalLine(p, p1, p2);
        /// 反向或者有个为 0
        if (GetDot(cross - p1, cross - p2) <= 0)
            return Vector3.Distance(p, cross);
        float pp1 = Vector3.Distance(p, p1);
        float pp2 = Vector3.Distance(p, p2);
        return pp1 > pp2 ? pp2 : pp1;
    }
    else
    {
        /// 第二种
        float pp1 = Vector3.Distance(p, p1);
        float pp2 = Vector3.Distance(p, p2);
        /// 点在直线上
        if (IsPointInLine(p, p1, p2))
        {
            if (GetDot(p - p1, p - p2) <= 0)
                return 0;
            if (GetDot(p - p1, p - p2) > 0)
                return pp1 > pp2 ? pp2 : pp1;
        }
        float p1p2 = Vector3.Distance(p1, p2);
        /// 钝角或者直角 pp1是最长边
        if (pp1 * pp1 >= pp2 * pp2 + p1p2 * p1p2)
            return pp2;
        /// 钝角或者直角 pp2是最长边
        if (pp2 * pp2 >= pp1 * pp1 + p1p2 * p1p2)
            return pp1;
        /// 根据三角形面积
        /// S=Sqrt(p(p-a)(p-b)(p-c))    p=(a+b+c)/2
        /// S=d*h/2 h为点到直线的距离
        /// h=2S/d
        float l = (pp1 + pp2 + p1p2) / 2;
        float S = Mathf.Sqrt(l * (l - pp1) * (l - pp2) * (l - p1p2));
        return 2 * S / p1p2;
    }
}

/// <summary>
/// 两直线交叉点
/// 0 两条线平行,2 两条线重合,1 有交点
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <param name="d"></param>
/// <returns></returns>
int LineCrossPoint(Vector3 a, Vector3 b, Vector3 c, Vector3 d, out Vector3 cross)
{
    cross = Vector3.zero;
    float k1 = 0, k2 = 0;
    int state = 0;
    if (a.x != b.x)
    {
        k1 = (b.z - a.z) / (b.x - a.x);
        state |= 1;
    }
    if (c.x != d.x)
    {
        k2 = (d.z - c.z) / (d.x - c.x);
        state |= 2;
    }
    switch (state)
    {
        case 0:
            /// 两条线都无斜率
            return a.x == c.x ? 2 : 0;
        case 1:
            /// l1 有斜率 l2 无斜率
            cross.x = c.x;
            cross.z = k1 * (cross.x - a.x) + a.z;
            return 1;
        case 2:
            /// l2 有斜率 l1 无斜率
            cross.x = a.x;
            cross.z = k2 * (cross.x - c.x) + c.z;
            return 1;
        case 3:
            /// 两条线都有斜率
            if (k1 == k2)
            {
                return GetCross(a, b, c) == 0 ? 2 : 0;
            }
            /// 斜率存在时
            /// k=(y-y1)/(x-x1)=>y=k(x-x1)+y1
            /// 交点:k1(x-x1)+y1=k2(x-x2)+y2=>x=(k1x1-k2x2-y1+y2)/(k1-k2)
            cross.x = (k1 * a.x - k2 * c.x - a.z + c.z) / (k1 - k2);
            cross.z = k1 * (cross.x - a.x) + a.z;
            return 1;
    }
    return 0;
}

/// <summary>
/// 点在直线的投影 平面
/// </summary>
/// 第一种
/// 斜率公式: k=(y-y1)/(x-x1)
/// 投影点在 p1p2 上:y=k*(x-p1x)+p1y
/// 投影点在 p 和投影点上斜率为 -1/k:y=(-1/k)*(x-p0x)+p0y
/// x=(p0x/k+p0y+k*p1x-p1y)/(1/k+k)
/// 第二种
/// p1p 在 p1p2 单位向量上的投影长度 dot(p1p,p1p2) 符号为方向
/// 结果 * p1p2 p1p投
/// p 投坐标 =p1p投+p1 
/// <param name="p"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
Vector3 PointVerticalLine(Vector3 p, Vector3 p1, Vector3 p2, bool useMethod = false)
{
    if (useMethod)
    {
        if (p1.x == p2.x)
            return new Vector3(p1.x, 0, p.z);
        if (p1.z == p2.z)
            return new Vector3(p.x, 0, p1.z);
        float k = (p1.z - p2.z) / (p1.x - p2.x);
        Vector3 cross = Vector3.zero;
        cross.x = (p.x / k + p.z + k * p1.x - p1.y) / (k + 1 / k);
        cross.z = k * (cross.x - p1.x) + p1.z;
        return cross;
    }
    else
    {
        float len = GetDot(p - p1, (p2 - p1).normalized);
        Vector3 cross = len * (p2 - p1).normalized;
        return cross + p1;
    }
}

/// <summary>
/// p1p2 x p1p
/// XZ平面叉乘公式 A={a1,b1} B={a2,b2} AxB=a1b2-a2b1
/// |p1p2|*|p1p|*sin 大于0在左,小于0在右
/// </summary>
/// <returns></returns>
float GetCross(Vector3 p1, Vector3 p2, Vector3 p)
{
    return (p2.x - p1.x) * (p.z - p1.z) - (p.x - p1.x) * (p2.z - p1.z);
}

/// <summary>
/// a * b 平面点乘 a={x1,y1} b={x2,y2} a*b=x1x2+y1y2
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
float GetDot(Vector3 a, Vector3 b)
{
    return a.x * b.x + a.z * b.z;
}
/// <summary>
/// 返回 pp1,pp2 点乘
/// </summary>
/// <param name="p"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
float GetDot(Vector3 p, Vector3 p1, Vector3 p2)
{
    return GetDot(p1 - p, p2 - p);
}

/// <summary>
/// 垂直向量 顺时针
/// V1=(a,b) V2=(b,-a)
/// </summary>
/// <param name="v"></param>
/// <returns></returns>
Vector3 GetVertical(Vector3 v)
{
    return new Vector3(v.z, 0, -v.x);
}

/// <summary>
/// 顺时针 向量旋转,旋转后的向量
/// </summary>
/// <param name="dir"></param>
/// <param name="angle"></param>
/// <param name="up">旋转轴 法向量</param>
/// <returns></returns>
public Vector3 GetRotateVector(Vector3 dir, float angle)
{
    return Quaternion.AngleAxis(angle, Vector3.up) * dir;
}
/// <summary>
/// 顺时针 b 绕 a 旋转后的坐标
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="angle"></param>
/// <param name="up"></param>
/// <returns></returns>
public Vector3 GetRotateVector(Vector3 a, Vector3 b, float angle)
{
    return Quaternion.AngleAxis(angle, Vector3.up) * (b - a) + a;
}
/// <summary>
/// 逆时针旋转公式:x=x0*cosB-y0*sinB y=x0*sinB+y0*cosB
/// 顺时针将角度变为 -B
/// </summary>
/// <param name="dir"></param>
/// <param name="angle"></param>
/// <param name="isShun"></param>
/// <returns></returns>
public Vector3 GetRotateVector(Vector3 dir, float angle, bool isShun)
{
    float radians = angle * Mathf.Deg2Rad;
    float sin = Mathf.Sin(radians);
    float cos = Mathf.Cos(radians);
    if (!isShun)
    {
        return new Vector3(dir.x * cos - dir.z * sin, dir.y, dir.x * sin + dir.z * cos);
    }
    else
    {
        return new Vector3(dir.x * cos + dir.z * sin, dir.y, -dir.x * sin + dir.z * cos);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值