什么是四元數(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è)原理。

練習(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ì)。

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

已知的條件是:玩家的半徑和位置、炸彈的位置。
我們需要實(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);
}
}

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

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

因?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)位置。