unity3d:向量计算,AOE图形相交

点到直线的最短距离

代码语言:javascript
复制
/// <summary>
        /// 三角函数法求x到直线x0为起点,u为单位向量的垂直最短距离平方
        /// </summary>
        /// <param name="x0">起点</param>
        /// <param name="u">射线的单位向量</param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static float StraightPointSqrMinDistanceByDir(Vector2 x0, Vector2 u, Vector2 x)
        {
            float t = Vector2.Dot(x - x0, u);
            return (x - (x0 + Mathf.Abs(t) * u)).sqrMagnitude;
        }

x0为起点,u为单位向量,则x0t的长度为 |x0x|cosa = x0xu / |u|,因为u为单位向量,模长为1。然后得到t点坐标为x - (x0 + Mathf.Abs(t) * u),因为x可能在x0的左边,所以只算长度的绝对值单位向量,然后算x,t两点距离

在这里插入图片描述

点到线段的距离

点落在线段之间为最短的垂直距离,否则为到两个端点之一的最短距离

代码语言:javascript
复制
/// <summary>
        /// 计算线段与点的平方距离,点在线段之间是垂直距离,否则是与最近端点距离
        /// </summary>
        /// <param name="x0"></param>
        /// <param name="u">线段方向至末端点,为两点相减</param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static float SegmentPointSqrDistance(Vector2 x0, Vector2 u, Vector2 x)
        {
            float t = Vector2.Dot(x - x0, u) / u.sqrMagnitude;
            return (x - (x0 + Mathf.Clamp(t, 0, 1) * u)).sqrMagnitude;
        }

1、首先假设已知直线上两点P1、P2、以及直线外一点P3。 2、令投影点为P0。 3、因为P0、P1、P2都在同一条直线上,所以可得k (P2 - P1) = P0 - P1 k = |P0-P1|/|P2-P1|。 只要求出比例因子k,便可求出P0的值。 4、令v1 = P3 - P1 , v2 = P2 - P1,v1与v2进行点乘得:v1v2=cos(seta)|P3-P1||P2-P1|=|P0-P1||P2-P1|,于是 k = |P0-P1|/|P2-P1| = ( (v1v2)/|P2-P1| ) / |P2-P1| = (P3 - P1) * (P2 - P1) / (|P2 - P1| * |P2 - P1|) 因为是到线段的距离,所以k的范围为[0,1], 投影点坐标 x0 + Mathf.Clamp(t, 0, 1) * u ,u为 x1 - x0

点是否在矩形内

外积,又称叉积,是向量代数(解析几何)中的一个概念。两个向量v1(x1, y1)和v2(x2, y2)的外积v1×v2=x1y2-y1x2。如果由v1到v2是顺时针转动,外积为负,反之为正,为0表示二者方向相同(平行)。

代码语言:javascript
复制
//外积。两个向量v1(x1, y1)和v2(x2, y2)的外积v1×v2=x1y2-y1x2。
        //>0,a在b顺时针方向    <0,a在b逆时针
        public static float Cross(this Vector2 a, Vector2 b)
        {
            return a.x * b.y - b.x * a.y;
        }
    public static bool IsPointInRectangle(Vector2 P, Vector2[] rectCorners)
    {
        return IsPointInRectangle(P, rectCorners[0], rectCorners[1], rectCorners[2], rectCorners[3]);
    }

    //矩形4个点,从第一个点开始逆时针或者顺时针排序
    public static bool IsPointInRectangle(Vector2 P, Vector2 A, Vector2 B, Vector2 C, Vector2 D)
    {
        Vector2 AB = A - B;
        Vector2 AP = A - P;
        Vector2 CD = C - D;
        Vector2 CP = C - P;

        Vector2 DA = D - A;
        Vector2 DP = D - P;
        Vector2 BC = B - C;
        Vector2 BP = B - P;

        bool isBetweenAB_CD = AB.Cross(AP) * CD.Cross(CP) &gt; 0;
        bool isBetweenDA_BC = DA.Cross(DP) * BC.Cross(BP) &gt; 0;
        return isBetweenAB_CD &amp;&amp; isBetweenDA_BC;
    }</code></pre></div></div><h2 id="dp24a" name="%E5%9C%86%E4%B8%8E%E5%9C%86%E7%9B%B8%E4%BA%A4">圆与圆相交</h2><p>两圆心距离平方 &lt; 两者半径长平方</p><h2 id="bj5fq" name="%E5%9C%86%E4%B8%8E%E7%9F%A9%E5%BD%A2%E7%9B%B8%E4%BA%A4">圆与矩形相交</h2><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">/// &lt;summary&gt;
    /// 圆与矩形是否相交
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;cc&#34;&gt;圆心&lt;/param&gt;
    /// &lt;param name=&#34;r&#34;&gt;圆半径&lt;/param&gt;
    /// &lt;param name=&#34;a&#34;&gt;&lt;/param&gt;
    /// &lt;param name=&#34;b&#34;&gt;&lt;/param&gt;
    /// &lt;param name=&#34;c&#34;&gt;&lt;/param&gt;
    /// &lt;param name=&#34;d&#34;&gt;&lt;/param&gt;
    /// &lt;returns&gt;&lt;/returns&gt;
    public static bool IsCicleRectIntersect(Vector2 cc,float r,Vector2 rectA,Vector2 rectB, Vector2 rectC, Vector2 rectD)
    {
        if (IsPointInRectangle(cc, rectA, rectB, rectC, rectD))//圆心在矩形内部
        {
            return true;
        }
        else//圆心在矩形外部,与任意一条边相交,即相交
        {
            float sqR = r * r;
            float disA = SegmentPointSqrDistance(rectA, rectB - rectA, cc);
            if (disA &lt; sqR)
            {
                return true;
            }

            float disB = SegmentPointSqrDistance(rectB, rectC - rectB, cc);
            if (disB &lt; sqR)
            {
                return true;
            }

            float disC = SegmentPointSqrDistance(rectC, rectD - rectC, cc);
            if (disC &lt; sqR)
            {
                return true;
            }

            float disD = SegmentPointSqrDistance(rectD, rectA - rectD, cc);
            if (disD &lt; r * r)
            {
                return true;
            }
        }
        return false;
    }</code></pre></div></div><p>圆心在矩形内即相交。圆心在矩形外,比较圆心到每条矩形边线段的距离,只要有一条&lt; 圆的半径即相交</p><h2 id="cv0ht" name="%E7%82%B9%E5%9B%B4%E7%BB%95%E5%8F%A6%E4%B8%80%E7%82%B9%E6%97%8B%E8%BD%AC%E5%90%8E%E5%9D%90%E6%A0%87">点围绕另一点旋转后坐标</h2><p>两个向量夹角</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">float angel = Vector2.Angle(Vector2.right, dirPos);

		if (dirPos.y &lt; 0)
		{
			angel = -angel;
		}</code></pre></div></div><p>一个向量与Vector.right的夹角

Vector2.Angle
第一象限:090
第二象限:90
180
第三象限:18090
第四象限:90
0
三四象限要为 负值旋转
旋转后坐标

代码语言:javascript
复制
public static Vector2 RotatePoint(Vector2 origin, float angle, Vector2 point)
{

// Translate point back to origin;

Vector2 temp = new Vector2(point.x -= origin.x, point.y -= origin.y);

// Roate the point

float xNew = Mathf.Cos(angle * Mathf.Deg2Rad) * (point.x) - Mathf.Sin(angle * Mathf.Deg2Rad) * (point.y);

float yNew = Mathf.Cos(angle * Mathf.Deg2Rad) * (point.y) + Mathf.Sin(angle * Mathf.Deg2Rad) * (point.x);

temp.x = xNew + origin.x;

temp.y = yNew + origin.y;

return temp;

}

圆与朝向矩形相交

先是使用rect的矩形,然后根据矩形朝向向量旋转rect的四个顶点

代码语言:javascript
复制
// 无旋转朝向矩形----->服务器以选重点为中心的矩形,客户端选中点在矩形边缘,unity中rect无法使用方向
Rect effRange = new Rect(selectedPos.x, selectedPos.y - rectHigh * .5f, rectWidth, rectHigh);
Vector2 pos1 = HXUtility.RotatePoint(selectedPos, angel, effRange.min);
Vector2 pos2 = HXUtility.RotatePoint(selectedPos, angel, effRange.min + new Vector2(effRange.width, 0));
Vector2 pos3 = HXUtility.RotatePoint(selectedPos, angel, effRange.min + new Vector2(0, effRange.height));
Vector2 pos4 = HXUtility.RotatePoint(selectedPos, angel, effRange.max);

再判断点与矩形相交

圆与朝向扇形相交

代码语言:javascript
复制
// 扇形与圆盘相交测试
// a 扇形圆心
// u 扇形方向(单位矢量)
// theta 扇形扫掠半角
// l 扇形边长
// c 圆盘圆心
// r 圆盘半径
public static bool IsCicleSectorIntersect(
Vector2 a, Vector2 u, float theta, float l,
Vector2 c, float r)
{
// 1. 如果扇形圆心和圆盘圆心的方向能分离,两形状不相交
Vector2 d = c - a;
float rsum = l + r;
if (d.sqrMagnitude > rsum * rsum)
return false;

        // 2. 计算出扇形局部空间的 p
        float px = Vector2.Dot(d, u);
        float py = Mathf.Abs(Vector2.Dot(d, new Vector2(-u.y, u.x)));//扇形单位方向向量逆时针转90度

        // 3. 如果 p_x &gt; ||p|| cos theta,两形状相交
        if (px &gt; d.magnitude * Mathf.Cos(theta * Mathf.Deg2Rad))
            return true;

        // 4. 求左边线段与圆盘是否相交
        Vector2 q = l * new Vector2(Mathf.Cos(theta * Mathf.Deg2Rad), Mathf.Sin(theta * Mathf.Deg2Rad));
        Vector2 p = new Vector2(px, py);
        return SegmentPointSqrDistance(Vector2.zero, q, p) &lt;= r * r;
    }</code></pre></div></div>