Unity開發中常用的基礎3D數學(向量,點乘,叉乘,矩陣,四元數,尤拉角)

GG_M發表於2018-08-04

在看了很多文章和書籍直後發現Unity中的數學知識是很重要的,想要做到遊戲的高真實度和高流暢度,數學是必不可少的,今天就來記錄一下基礎的3D數學在Unity中開發時的運用,若文章中所寫有不足,歡迎指出留言,互相交流,感謝!

轉載請標註轉載地址


一、Unity中的向量

向量這個詞大家應該不陌生,在數學裡向量就是一個方向,沒有大小,同樣,在Unity中我們可以根據向量去獲取方向。比如說,我想把a物體移動到b物體所在的位置,那麼首先我們要做的就是先確定我們前進的方向,在確立了方向之後,就可以按照我們想要的速度向b目標點移動了,Unity中使用向量的加法來實現物體位移,程式碼如下:

    /// <summary>
    /// 向量的加法(從a位置移動到b位置)
    /// </summary>
    public void Add()
    {
        //先計算二者方向 
        Vector3 dir = (target.position - cube.position).normalized;
        //開始從a位置移動到b位置
        cube.position = cube.position + dir * Time.deltaTime * 3;
    }

其中,(target.forward - cube.forward)可以求得一個向量值,此處可以將該向量理解為有方向有大小的向量,normalized表示該向量長度為1,向量沒有大小,所以如果光是向量的話並不能完全表示朝向,將其大小設定為noemalized可以很好的表示出向量的朝向。

Unity中對於向量最好的一個例子是Vector3.Distance(),這個方法很常用,用來求得兩個物體之間的距離,或者作為攻擊範圍等來使用,其實它的原理就是用兩個向量相減求平方根得到的距離值,程式碼如下:

    /// <summary>
    /// 向量的減法(計算a,b兩物體間的距離)
    /// </summary>
    public void Sub()
    {
        float dic = Vector3.Distance(cube.position, target.position);
        print("距離為:" + dic);
    }


二、向量的點乘和叉乘

很多地方出現過這麼一句話:向量的點乘(Dot)判斷角度,向量的叉乘(Cross)判斷方向

先說點乘,點乘在Unity中的使用是用來計算兩個物體之間角度,用Vector2.Dot()或Vector3.Dot()方法可以得到一個弧度制,也就是這兩個物體夾角的弧度值,可以使用Mathf.Acos(弧度制)*Rad2Deg來轉換為夾角的角度值。對於點乘的理解2D的理解起來比較簡單,數學中的勾股定理,當知道斜邊長度為a,一條直角邊長度為b,夾角為R,那麼b = a*cosR,如下圖所示:

叉乘判斷方向,叉乘是兩物體的法向量,舉個例子,當我們的主角沒有面對怪物時,我們可以先用點乘計算出當前主角的朝向和怪物當前朝向的角度,然後用叉乘我們可以判斷出怪物是在我們的前?後?左?右?根據右手定則,當指向我們自己時,四指是逆時針,指向我們面朝方向時四指是順時針,無論當前指向哪裡,超過180度時都會變換朝向,所以叉乘時時刻刻都會告訴我們怎麼轉是最近的,Unity中我們用Vector3.Cross()計算兩物體的叉乘,返回值是一個Vector3型別,最大為1最小為-1。我們可以用一個案例來理解點乘和叉乘:

public class SteeringWheelScript : MonoBehaviour {
    private GameObject image;
    private Vector3 imageTra;

    //滑鼠點向量
    Vector3 mouseVec;
    //上一幀滑鼠點向量
    Vector3 beforeMouseVec;
    //滑鼠點上一幀位置
    Vector3 beforeMousePos;

    private void Awake()
    {
        image = GameObject.Find("Canvas/Image");
        imageTra = image.transform.position;
        //beforeMousePos = imageTra;
    }

    /// <summary>
    /// 得到滑鼠座標
    /// </summary>
    /// <returns>返回當前滑鼠位置</returns>
    public Vector3 GetMousePos()
    {
        if (EventSystem.current.IsPointerOverGameObject()&&Input.GetMouseButton(0)&&!Input.GetMouseButtonUp(0))
        {
            return Input.mousePosition;
        }
        else
            return default(Vector3);
    }

    /// <summary>
    /// 開始轉動
    /// </summary>
    public void TurnRun(Vector3 currentMousePos)
    {
        //得到向量
        mouseVec = (currentMousePos - imageTra).normalized;
        beforeMouseVec = (beforeMousePos - imageTra).normalized;

        //計算方向
        Vector3 direction = Vector3.Cross(beforeMousePos.normalized, beforeMouseVec.normalized);
        print("方向:" + direction.z);
        //計算角度
        float radianValue = Vector3.Dot(beforeMouseVec.normalized, currentMousePos.normalized);//得到弧度值
        float angleValue = Mathf.Acos(radianValue) * Mathf.Rad2Deg;//角度
        print("角度:" + angleValue);
        //順時針轉
        if (direction.z > 0)
        {
            image.transform.eulerAngles = new Vector3(0,0, angleValue);
        }
        //逆時針
        else if (direction.z < 0)
        {
            image.transform.eulerAngles = new Vector3(0, 0, -angleValue);
        }
        beforeMousePos = currentMousePos;
    }

    private void FixedUpdate()
    {
        TurnRun(GetMousePos());
    }
}


這個是模擬方向盤的轉動,這裡我用的是UGUI,Unity版本是5.6,EventSystem.current.IsPointerOverGameObject()是用來判斷當前滑鼠是否在UI上面。


三、矩陣

矩陣在3D引擎底層實現中使用的非常多,對於矩陣,我們從固定流水線開始理解,固定流水線是指一個3D物體在顯示器上成像的過程,在這個過程中就用到了非常多的矩陣轉換。用一個比較好理解的方式來說吧,首先,當我們需要一個3D模型時,會讓美工開始在3DMax裡面製作,製作完成匯出FBX格式,當前這個模型就是一個個體,也就是區域性座標,然後我們會把模型匯入Unity中調整位置,進行拖拽,這個過程使得模型從單個個體變為了Unity中模型的其中之一,也就是世界座標,此時我們要看到該模型,所以新增攝像機,把模型放在攝像機視野內,這樣,又從世界座標變成了觀察座標,當然,攝像機看不到的模型背面就會被優化掉,那麼觀察座標就變成了裁剪座標,之後為了把模型顯示在螢幕上,又把裁剪座標變為視口座標,最後進行光柵化完成固定流水線。

以上是對矩陣的一個簡單瞭解,物體的縮放,旋轉,位移,雖然我們直接用Rotate等函式就可以很便捷的完成,但是其底層實現也是矩陣的變換,這裡說一個小點,3D空間中,點是三維的,而矩陣是四維,這裡涉及到了齊次座標,進行矩陣計算時,會把三維的點轉變成齊次座標然後才可以計算,因為像位移,旋轉,縮放等三維矩陣是完成不了的,點轉為齊次座標時會用1補齊第四位比如(x,y,z,1)


四、四元數

四元數在Unity中主要用於做旋轉使用,這裡說幾個很常用的四元數方法,Quaternion.Eular()可以獲取物體的四元數,Rotation就是一個四元數,但是不可以直接對它進行賦值,這裡可以用到eularAngle 進行賦值,控制物體的旋轉可以用AngleAxis()方法進行操作。


五、尤拉角

使用尤拉角進行旋轉賦值時一定可能性會出現萬向節死鎖,什麼是萬向節死鎖,其實萬向節死鎖的原因是尤拉角本身,尤拉角旋轉賦值一般情況下是一對一,比如,旋轉X軸,旋轉Y軸,旋轉Z軸,但是在一些情況下會變成一對多,比如旋轉Z軸,X軸和Z軸進行了旋轉使得整體旋轉發生改變,X軸的旋轉自由缺失,這種情況最好的避免方式是,僅對一個軸進行賦值,其餘軸為0。直接使用尤拉角的程式碼效率還是比較高的。


到此Unity中比較常用的基礎3D數學也就介紹完了,整體我是沿著在Unity開發中的使用來介紹的,如果大家對其中某些部分有興趣可以去查閱一下資料,深挖一下。

相關文章