unity-四元數(shù)

什么是四元數(shù)?

歐拉角和四元數(shù),都是表示物體旋轉(zhuǎn)的。但歐拉角有個(gè)萬向節(jié)死鎖的問題,為了解決這個(gè)問題,因此引入了四元數(shù)。
四元數(shù),就是用4個(gè)數(shù)字來表示旋轉(zhuǎn)。但這四個(gè)數(shù)就不是角度了。假設(shè)我們的旋轉(zhuǎn)軸是V軸,那這四個(gè)數(shù)字分別是:
x=sin(θ/2)*Vx
y=sin(θ/2)*Vy
z=sin(θ/2)*Vz
w=cos(θ/2)
這四個(gè)數(shù)字的取值范圍是-1到1。
四元數(shù)可以與歐拉角進(jìn)行無縫切換。有了它,我們就可以解決任意角度的旋轉(zhuǎn)問題了。

using UnityEngine;
using System.Collections;

public class VectorDemo2 : MonoBehaviour {

    // Use this for initialization
    void Start () {
    
    }
    
    // Update is called once per frame
    void Update () {
    
    }

    private void OnGUI(){
        if (GUILayout.Button ("設(shè)置物體旋轉(zhuǎn)角度")) {
            Quaternion qt = new Quaternion();
            this.transform.rotation = Quaternion.Euler(0,60,0);
        }
        if (GUILayout.RepeatButton ("沿x軸旋轉(zhuǎn)")) {
            Quaternion qt = new Quaternion();
            this.transform.rotation *= Quaternion.Euler(1,0,0);
        }
        if (GUILayout.RepeatButton ("沿y軸旋轉(zhuǎn)")) {
            Quaternion qt = new Quaternion();
            this.transform.rotation *= Quaternion.Euler(0,1,0);
        }
        if (GUILayout.RepeatButton ("沿z軸旋轉(zhuǎn)")) {
            Quaternion qt = new Quaternion();
            this.transform.Rotate(0,0,1);
        }
    }
}

Quaternion.Euler這個(gè)函數(shù),可以將歐拉角直接轉(zhuǎn)為四元數(shù)。
四元數(shù)左乘向量,其實(shí)就是旋轉(zhuǎn)角度的疊加,Rotate方法其實(shí)就是這個(gè)原理。


GIF 2022-12-30 9-39-49.gif

練習(xí)

計(jì)算當(dāng)前物體右前方30度、10米遠(yuǎn)的敵人位置。

using UnityEngine;
using System.Collections;

public class VectorDemo2 : MonoBehaviour {
    public Vector3 vect;
    // Use this for initialization
    void Start () {
    
    }
    
    // Update is called once per frame
    void Update () {
        Debug.DrawLine (this.transform.position,vect);
        if (Input.GetMouseButtonDown (0)) {
            vect = this.transform.rotation * new Vector3(0,0,10);//根據(jù)當(dāng)前物體的旋轉(zhuǎn)而旋轉(zhuǎn)
            vect = Quaternion.Euler(0,30,0) * vect;//vect向量沿y軸旋轉(zhuǎn)30度
            vect = this.transform.position + vect;//vect向量移動(dòng)到當(dāng)前位置
        }
    }


}

再來一個(gè)復(fù)雜點(diǎn)的——炸彈攻擊范圍的判斷。
比如炸彈爆炸范圍為半徑10米,在這個(gè)范圍內(nèi)就會(huì)被炸到,反之則不會(huì)。


8.png

如果僅判斷玩家中心點(diǎn)和炸彈的距離比較容易。但考慮到障礙物,有時(shí)玩家可以躲到墻體后面只露半個(gè)身體,這時(shí)又如何判斷呢?
那就要判斷玩家身體的具體位置跟炸彈之間的距離了。
如果頭部位置跟炸彈的距離在爆炸范圍內(nèi),就會(huì)掉血;
而頭部和足部位置都在爆炸范圍內(nèi),那就會(huì)死亡。
隨著玩家的不斷跑動(dòng),我們需要實(shí)時(shí)計(jì)算玩家的頭部和足部的位置,然后不斷跟炸彈的距離做比較。


8.png

已知的條件是:玩家的半徑和位置、炸彈的位置。
我們需要實(shí)時(shí)計(jì)算炸彈和玩家形成的黃色切線坐標(biāo),然后跟炸彈的位置做比較,是否也在10米爆炸范圍內(nèi)?
一旦抽象為數(shù)學(xué)問題就比較容易了。如圖所示,切線形成一個(gè)直角三角形,這樣就確定了一個(gè)角;而由炸彈和玩家位置,就可以確定斜邊的距離。已知一邊一角,就可以計(jì)算玩家到切線的那個(gè)夾角。

有了這個(gè)夾角,我們就可以旋轉(zhuǎn)當(dāng)前向量,從而得到切線向量(也就是坐標(biāo)),有了這個(gè)坐標(biāo),再跟炸彈進(jìn)行比較,就可以判斷出是否在爆炸范圍內(nèi)了。

using UnityEngine;
using System.Collections;

public class VectorDemo2 : MonoBehaviour { //掛載到炸彈物體下
    public string playerTag = "Player";//玩家標(biāo)簽,根據(jù)這個(gè)標(biāo)簽查找物體
    public Transform playerTF;
    public float radius;//玩家半徑
    private Vector3 leftTan,rightTan;
    // Use this for initialization
    void Start () {
        GameObject player = GameObject.FindWithTag (playerTag);
        playerTF = player.transform;
        radius = playerTF.GetComponent <CapsuleCollider>().radius;//獲取玩家的半徑,玩家用圓柱體代替
    }
    
    // Update is called once per frame
    void Update () {
        CalcPosition ();
    }
    public void CalcPosition(){
        Vector3 playerToExplosion = this.transform.position - playerTF.position;
        Vector3 playerToExplosionDir = playerToExplosion.normalized * radius;
        float angle = Mathf.Acos (radius / playerToExplosion.magnitude)* Mathf.Rad2Deg;//已知一邊一角,求角度,注意弧度轉(zhuǎn)角度
        Vector3 leftRoll = Quaternion.Euler (0, -angle, 0) * playerToExplosionDir;//當(dāng)前玩家左旋轉(zhuǎn)
        Vector3 rightRoll = Quaternion.Euler (0, angle, 0) * playerToExplosionDir;//當(dāng)前玩家右旋轉(zhuǎn)
        leftTan = playerTF.position + leftRoll;//轉(zhuǎn)換坐標(biāo)
        rightTan = playerTF.position + rightRoll;
        Debug.DrawLine (this.transform.position,leftTan);
        Debug.DrawLine (this.transform.position,rightTan);
    }


}

9.png

新建一個(gè)cube當(dāng)炸彈,然后將腳本掛載過去。
然后新建一個(gè)圓柱體當(dāng)玩家,標(biāo)簽可以直接選擇Player。


78.png

圓柱體的半徑在Capsule Collider這個(gè)組件下,可以更改這個(gè)值。
從炸彈尋找玩家,可以通過GameObject.FindWithTag這個(gè)方法查找到;
從玩家對(duì)象中尋找一個(gè)組件,可以用GetComponent方法。
最后一步,轉(zhuǎn)換坐標(biāo)這塊不是很好理解。


3.png

因?yàn)槲覀冏隽讼蛄繕?biāo)準(zhǔn)化,因此,當(dāng)玩家向量第一次進(jìn)行旋轉(zhuǎn)后,計(jì)算出來的向量是一個(gè)世界坐標(biāo),也就是從原點(diǎn)處出發(fā)的向量。這個(gè)向量的位置不對(duì),因?yàn)檫@時(shí)如果移動(dòng)玩家的位置,這個(gè)向量是不會(huì)跟著變化的。
我們需要的是圖示中的紅色向量位置。因此要跟玩家的向量相加,才能得到我們想要的坐標(biāo)位置。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容