【Unity】向量點乘與叉乘

VinChyy發表於2017-11-20

【參考原文】Unity3D之Vector3.Dot和Vector3.Cross的使用
【參考原文】Unity3D之點乘和差乘
  在Unity3D中,Vector3.Dot表示求兩個向量的點積;Vector3.Cross表示求兩個向量的叉積。
  
  點積計算的結果為數值,而叉積計算的結果為向量。兩者要注意區別開來。
  
  簡單的說,點乘判斷角度,叉乘判斷方向。

  形象的說當一個敵人在你身後的時候,叉乘可以判斷你是往左轉還是往右轉更好的轉向敵人,點乘得到你當前的面朝向的方向和你到敵人的方向的所成的角度大小。

1.點積

a·b=|a|·|b|cos<a,b>

  • |a|和|b|表示向量的模
  • <a,b>表示兩個向量的夾角。<a,b>和<b,a> 夾角是不分順序的。
  • 通過點積可以計算兩個向量的夾角的。

如果兩個向量a,b均 為單位 向量 ,那麼a.b等於向量b在向量a方向上的投影的長度

應用

  通過點積的計算我們可以簡單粗略的判斷當前物體是否朝向另外一個物體: 只需要計算當前物體的transform.forward向量與 (otherObj.transform.position – transform.position)的點積即可。
- 若結果 == 0,則兩向量互垂直 。
- 若結果 > 0 ,則兩向量夾角小於90°,當前物體面對
- 若結果 < 0 ,則兩向量夾角大於 90°,當前物體背對

2.叉積

c =a x b

  • 其中a,b,c均為向量。即兩個向量的叉積得到的還是向量!
  • 性質1:c⊥a,c⊥b,即向量c垂直與向量a,b所在的平面。
  • 性質2:模長|c|=|a||b|sin<a,b>
  • 性質3:滿足右手法則。從這點我們有axb ≠ bxa,而axb = – bxa。所以我們可以使用叉積的正負值來判斷向量a,b的相對位置,即向量b是處於向量a的順時針方向還是逆時針方向。

求兩向量夾角

using UnityEngine;  
using System.Collections;  

public class MainScript : MonoBehaviour  
{  
        //向量a  
        private Vector3 a;  
        //向量b  
        private Vector3 b;  

        void Start ()  
        {  
                //向量的初始化  
                a = new Vector3 (1, 2, 1);  
                b = new Vector3 (5, 6, 0);  
        }  

        void OnGUI ()  
        {  
                //點積的返回值  
                float c = Vector3.Dot (a, b);  
                //向量a,b的夾角,得到的值為弧度,我們將其轉換為角度,便於檢視!  
                float angle = Mathf.Acos (Vector3.Dot (a.normalized, b.normalized)) * Mathf.Rad2Deg;  
                GUILayout.Label ("向量a,b的點積為:" + c);  
                GUILayout.Label ("向量a,b的夾角為:" + angle);  


                //叉積的返回值  
                Vector3 e = Vector3.Cross (a, b);  
                Vector3 d = Vector3.Cross (b, a);  
                //向量a,b的夾角,得到的值為弧度,我們將其轉換為角度,便於檢視!  
                angle = Mathf.Asin (Vector3.Distance (Vector3.zero, Vector3.Cross (a.normalized, b.normalized))) * Mathf.Rad2Deg;  
                GUILayout.Label ("向量axb為:" + e);  
                GUILayout.Label ("向量bxa為:" + d);  
                GUILayout.Label ("向量a,b的夾角為:" + angle);  
        }  
}  

上面的示例中,我們定義了兩個向量a和b。分別求出了他們的點積和叉積,並通過點積和叉積來反過來計算他們的夾角。
這裡要說明的是:
1.a.normalized 和 b.normalized 表示的是兩個向量的單位向量, 因為在公式裡,有向量和模的除法,得出來的結果就是單位向量(單位向量模為1),所以我們這裡和後面都直接用單位向量來計算,省去不少麻煩。

2.Mathf.Rad2Deg表示的是 單位弧度的度數。也就是2π/360

3.通過叉積計算度數是通過公式|c|=|a||b|sin<a,b>來逆向求值。|c| 其實就是叉積的模,換句話說,也代表著Vector3.Distance (Vector3.zero, Vector3.Cross (a.normalized, b.normalized))的值。
結果圖如下:
這裡寫圖片描述

點乘與叉乘專案中的應用

專案需求:
1 通過點乘計算物體B在物體A的前方還是後方
2 通過叉乘,計算物體A轉向物體B時,最小角的旋轉方向

實現過程:
首先計算出物體A的前方朝向向量v_Bz=B.transform.forward,然後計算物體B相對於物體A的位置向量 v_AB = A.position - B.position;

通過計算v_Bz與v_AB向量點乘結果的正負,判斷物體B在物體A的前後
如果物體B在物體A前方, Vector3.Dot(v_Bz,v_AB)大於0
如果物體B在物體A後方, Vector3.Dot(v_Bz,v_AB)小於0

通過計算v_Bz與v_AB向量叉乘的結果,判斷旋轉方向
如果逆時針旋轉,v_C = Vector3.Cross(v_Bz, v_AB)為沿y軸負方向
如果順時針旋轉,v_C = Vector3.Cross(v_Bz, v_AB)為沿y軸正方向

using UnityEngine;
using System.Collections;

public class Vector3_Dot : MonoBehaviour {

    public Transform A, B;
    Vector3 v_Bz, v_AB, v_A,v_B,v_C;
    string str = "";



    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {


        //點乘
        v_Bz = B.transform.forward;//B.transform.TransformDirection(Vector3.forward);
        v_AB = A.position - B.position;
        float f = Vector3.Dot(v_Bz,v_AB);
        if (f>0)
        {
            str = "A在B自身座標系的前方";
        }
        else if (f<0)
        {
            str = "A在B自身座標系的後方";

        }
        else
        {
            str = "A在B自身座標系的左前方或右方";
        }

        //差乘
        v_A = A.position;
        v_B = B.position;
        v_C = Vector3.Cross(v_Bz, v_AB);
       // A.Rotate(0,0,0);

    }

    void OnGUI()
    {
        GUI.Label(new Rect(10,10,200,60),str);

    }



    void OnDrawGizmos()
    {
        //差乘繪製相關線
        Gizmos.color = Color.blue;
        Gizmos.DrawLine(-v_C,Vector3.zero);



        //點乘繪製相關線
        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(Vector3.zero,A.position);

        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(Vector3.zero,B.position);

        Gizmos.color = Color.red;
        Gizmos.DrawLine(A.position, B.position);

        Gizmos.color = Color.red;
        Gizmos.DrawLine(Vector3.zero,v_AB);

        Gizmos.color = Color.green;
       // Gizmos.DrawLine(A.transform.position, Vector3.left);

    }
}

這裡寫圖片描述

總結

1.判斷目標在自己的前後方位可以使用下面的方法:

Vector3.Dot(transform.forward, target.position-transform.position)

返回值為正時,目標在自己的前方,反之在自己的後方

2.判斷目標在機子的左右方位可以使用下面的方法:

Vector3.Cross(transform.forward, target.position-transform.position).y

返回值為正時,目標在自己的右方,反之在自己的左方

相關文章