Unity筆記-08-三維數學
Unity里的三維數學
1. 向量
所有物體的Position屬性指的都是從世界坐標原點到位置坐標的向量
代碼:position.magnitude;可以獲得此向量的模
代碼:position.normalized; 可以獲得與此向量同向的單位向量
這里可以說明:Translate()的移動方法的引數就可以給予一個向量,對應物體就會沿著該向量方向移動大小等同于該向量模大小的距離
通過代碼:Direction=A.position-B.position 就可以獲得兩個物體的向量差,向量指向被減數,方向指向A
將這個方向向量給到B.Translate()方法,就可以使得B沿著去往A的方向到達A這個點的位置
2. 三角函式
坐標系轉換
代碼:Vector WorldPoint=transform.TransformPoint(Vector3 E)
引數E為物體自身坐標系的對應位置,該方法將會回傳物體自身坐標系對應E位置的點在世界坐標系中的位置
獲得物體右前放30度,10米遠的坐標點
float x = Mathf.Sin(30 * Mathf.Deg2Rad) * 10;
float z = Mathf.Cos(30 * Mathf.Deg2Rad) * 10;
Vector3 point = transform.TransformPoint(x, 0, z);
3. 向量點乘和叉乘的應用
float dot = Vector3.Dot(A.normalized,B.normalized);//單位向量的點積即為兩個向量之間的cos值
float angle = Mathf.Acos(dot);
Vector3.normalized 方法可以獲得此向量方向上的單位向量,A向量上的單位向量和B向量上的單位向量的積即為兩個向量夾角的cos值,通過Acos方法可獲得兩個向量間的夾角,但是這種方法獲得的夾角只能在0-180度以內
如果需要獲得一圈大于180度的夾角就必須用到叉乘
float dot = Vector3.Dot(A.position.normalized, B.position.normalized);
angle = Mathf.Acos(dot)*Mathf.Rad2Deg;//計算180內的夾角
if (cross.y < 0)//判斷方位
{
angle = 360 - angle;
}
兩個向量的叉乘會因為二者的方位不同導致叉乘結果向量的方向相反,通過判斷叉乘結果向量的方向可以判斷A向量和B向量的左右方位,也能夠獲得大于180度的外夾角
應用場景

當玩家走進這個敵人弧形120度檢測范圍內才會受到敵人攻擊,該如何檢測?
首先需要判斷敵人的朝向向量和由敵人指向玩家的向量之間的角度,判斷是否進入角度范圍,通過點乘獲得角度,在通過叉乘判斷左右方位,如果要加距離判斷,那么只需要判斷玩家與敵人之間的距離即可,
那么敵人的朝向向量如何獲取?
一般情況下,為了方便,敵人的朝向向量設定為自身坐標系的Z軸或X軸,只需要代碼:
transform.TransformPoint(0,0,1)
或者更加簡便的代碼:
transform.forward
注意:敵人的朝向向量需要的是在世界坐標系中的向量,因此需要將敵人以自身坐標系的朝向向量轉化為世界坐標系中的向量坐標,上述兩種代碼做的是同樣的事情,都做到了轉化,
敵人指向玩家的向量如何獲取?
代碼:Vector3 distance=Player.position-Enemy.position
向量相減即可,注意向量相減的朝向指向被減向量
4. 歐拉角
使用三個角度來保存方位,X與Z軸以自身坐標系為準旋轉,Y軸則以世界坐標系旋轉
API:Vector3 eulerAngle=transform.eulerAngles;
優點
僅僅用三個數字表達方位,占用空間小
沿坐標軸旋轉的單位為角度,符合人的思考方式
任意三個數字都是合法的,不存在不合法的歐拉角
缺點
但是方位的表達不唯一,對于一個方位,存在多個歐拉角描述,因此無法判斷多個歐拉角代表的角位移是否相同
例如:0`5`0 和 0`365`0
? 0`-5`0 和 0`355`0
為了保證任意方位都只有獨一無二的表示,Unity引擎限制了角度范圍,即沿X軸旋轉限制在-90到90之間,沿Y與Z旋轉限制在0到360之間,不過Unity最新版本有所改動
注意:歐拉角雖然也是Vector3資料型別但是,它沒有方向沒有大小的概念,他的x-y-z表示的是各個軸向 上的旋轉角度,這一點注意和position位置區分,他們雖然都是Vector3資料型別,但是意義不同,
position有方向(從世界原點指向當前位置),有大小(世界原點到當前位置的直線距離),他的x-y-z表示的是各個軸向 上的有向位移
萬向節死鎖
當X軸的旋轉度為±90度的時候,此時,物體自身的Z軸和世界坐標系的Y軸重合,此時若再沿Z或Y旋轉將失去一個自由度,在這種情況下,規定沿著Z軸完成繞豎直軸的全部旋轉,即此時Y軸為0
由此我們看到歐拉角是有缺陷的,并且自身無法解決這個問題,因此我們要學四元數,它可以解決這個問題
5. 四元數
Quaternion在3D圖形學中代表旋轉,由一個三維向量(X/Y/Z)和一個標量W組成
旋轉軸為V(Vector3),旋轉弧度為θ,如果使用四元數表示,那么四個分量為
x=sin(θ/2)*V.x
y=sin(θ/2)*V.y
z=sin(θ/2)*V.z
w=cos(θ/2)
x y z w 的取值范圍為 -1 ~ 1
API:Quaternion qt=transform.rotation
代碼:Quaternion.Euler(Vector3 eulerAngle)
此方法可以通過歐拉角轉化為四元數進行旋轉
代碼:tramsform.Rotate(Vector3 eulerAngle)
此方法也是四元數變換,不會有萬向節死鎖的問題,此代碼等價于
transform.rotation*=Quaternion.Euler(Vector3 eulerAngle)
代碼:Vector3 newVec = Quaternion.AngleAxis(angle,axis)*oriVec
angle:旋轉度數
axis:圍繞哪個軸旋轉
oriVec:初始向量
此方法可使得某個向量繞任意軸旋轉任意角度
四元素相乘
兩個四元數相乘可以達到組合旋轉的作用
Quaternion A=Quaternion.Euler(0,20,0)*Quaternion.Euler(0,30,0);
//等價于下面這個代碼
Quaternion B=Quaternion.Euler(0,50,0);
與向量相乘
四元數左乘向量,表示將該向量按照四元數表示的角度在世界坐標系中旋轉對應角度,例如:
Vector3 point=new Vector3(0,20,0);
Vector3 newPoint=Quaternion.Euler(0,10,0)*point;
優點
可以避開萬向節死鎖
缺點
難于使用,不建議單獨修改某個值
存在不合法的四元數
舉例與說明
//注意所有的旋轉正為順,負為逆
transform.rotation *= Quaternion.Euler(1, 0, 0);//自身X軸旋轉1度
transform.position = Quaternion.Euler(1, 0, 0) * transform.position;//繞世界坐標軸旋轉1度
transform.position = transform.position + Quaternion.Euler(30, 0, 0) * new Vector3(0, 1, 0);//指定朝向移動:此處為沿Y軸順時針偏移30度的方向移動1單位
transform.position = transform.position + Quaternion.Euler(30, 0, 0) *transform.rotation* new Vector3(0, 1, 0);//跟隨物件旋轉朝向而做出回應變化
炸彈爆炸的范圍判定
假定玩家為一個圓柱體,有身體半徑,判定范圍通過炸彈到玩家的左右切線的模長判斷玩家處于炸彈的范圍區域
獲得左右切線
private Transform Player;
private float radius;//半徑
private Vector3 leftPoint;//左切點
private Vector3 rightPoint;//右切點
private float angle;//角度
private void Start()
{
//這里儲存的是參考,玩家位置變化,這里也會變化
Player = GameObject.FindObjectOfType<PlayerTag_1>().transform;
radius = Player.GetComponent<CapsuleCollider>().radius;
}
/// <summary>
/// 傷害范圍判定
/// </summary>
private void DamageVote()
{
//向量作差獲得炸彈和玩家之間的方向向量
Vector3 distance =transform.position-Player.position;
//將此向量的模與玩家半徑一致
Vector3 direction = distance.normalized * radius;
//獲得距離向量與切點向量之間的角度
angle = Mathf.Acos(radius/distance.magnitude)*Mathf.Rad2Deg;//算出來的是弧度,一定要記得轉化為角度!!!
//旋轉角度,獲得切點向量
rightPoint = Quaternion.Euler(0, angle, 0) * direction;
leftPoint = Quaternion.Euler(0, -angle, 0) * direction;
rightPoint = Player.position + rightPoint;//注意這里一定要加上玩家位置,因為單純的向量是從世界原點開始畫的
//注意:如果表示方向,單純的向量不會出錯,如果表示位置單純的向量通常會出錯,因為單純的向量是從世界原點開始畫的,而正確的位置應該要加上初始位置向量
leftPoint = Player.position + leftPoint;
//這里為了除錯直接畫出來
Debug.DrawLine(rightPoint, transform.position,Color.red);
Debug.DrawLine(leftPoint, transform.position, Color.red);
}
private void Update()
{
DamageVote();
}
Vector3(常用方法說明)
float angle = Angle(Vector a, Vector b);
回傳a向量和b向量之間的夾角
Vector3 b = Vector3.ClampMagnitude(a, float length);
回傳一個和a向量同方向的b向量,模長為length,但是最大模長不會超過a
Vector3 c = Vector3.Cross(Vector a, Vector b);
回傳a與b的叉乘
float distance = Vector3.Distance(Vector a, Vector b);
回傳a與b坐標之間的直線距離
float c = Vector3.Dot(Vector a, Vector b)
回傳a與b之間的點乘結果
Vector3 c = Vector3.Lerp(Vector a, Vector b, float t)
回傳兩個點之間的線性插值
通過插值t在點a和點b之間插值,引數t固定在范圍[0,1]內,這通常用于沿兩個端點之間的直線找到一個點(例如,在這些點之間逐漸移動物件),
回傳的值等于a+(b-a)*t(也可以寫為a*(1-t)+b*t),
當t=0時,Vector3.Lerp(a,b,t)回傳a,
當t=1時,Vector3.Lerp(a,b,t)回傳b,
當t=0.5時,Vector3.Lerp(a,b,t)回傳a和b之間的中點,
如果你傳的t超過1,則按照1處理,這里要提到另外一個方法:
Vector3 c = Vector3.LerpUnclamped(Vector a, Vector b, float t)
該方法的t允許超過1
自然運動除錯,可以在UnityInspector界面通過Curve界面通過函式影像除錯速度
transform.position = Vector3.Lerp(transform.position, target, curve.Evaluate(x));
通過AnimationCurve來通過函式控制物體的移動速度
Vector3 force = Vector3.Project(Vector3 a, Vector3 b);
引數解釋:a為目標向量,b為投影向量,回傳值為a在b上的投影向量
Vector3 force = Vector3.ProjectOnPlane(Vector3 a, Vector b)
引數解釋:a為目標向量,b為平面法向量,回傳值為a在b對應過世界原點平面的投影
Vector3 result = Vector3.Reflect(Vector3 a, Vector3 b)
引數解釋:a為目標向量,b為法線方向向量,回傳值為a以b為法線反射出去的反射光線向量
Vector3 result = Vector3.MoveTowards(Vector3 a, Vector3 b, float speed)
一般用于物體的勻速移動,a向量為起點,b向量為目標點,speed為移動速度,一般如此使用:
transform.position = Vector3.MoveTowards(transform.position, target, speed)
Quaternion(常用方法說明)
Quaternion.Euler(Vector3 angle)
以歐拉角構建四元數,使該四元數變化以該歐拉角旋轉,該旋轉以世界坐標為準旋轉,回傳該旋轉
四元數如何轉換歐拉角?代碼:
Vector3 eulerAngle=qt(Quaternion).eulerAngles;
Quaternion.AngleAxis(float angle, Vector3 axis);
angle為角度,axis為軸向,意為該四元數變化繞著axis軸向旋轉angle角度,注意angle為角度不是弧度,回傳該旋轉
Quaternion.LookRotation(Vector3 target)
當前物件注視旋轉,target為從物件指向目標的方向向量,一般為目標向量減去物件向量,回傳朝向目標方向的旋轉
注意:這種注視旋轉的注視方向為z軸,一般情況下物體的rotation朝向為z軸,如果要設定x軸朝向,可以將方向向量賦值給transform.right
Quaternion.lerp(Quaternion a, Quaternion b, float t);差值旋轉
a為初始四元數表示的旋轉朝向,b為終點四元數表示的旋轉朝向,t為比例
一般聯合LookRotation方法一起使用,通過LookRotation獲得旋轉終值,再通過此方法達到逐漸旋轉的效果,而非瞬間旋轉,但是差值旋轉只能無限接近,卻不能相等,因此需要判斷如果一個旋轉及其接近那么便直接將終點四元數直接賦值
這個接近判斷一般使用Quaternion.Angle方法判斷角度大小,當角度小于某個值的時候就算已經旋轉到了
Quaternion.RotationTowards(Quaternion a, Quaternion b, float speed)勻速旋轉
引數解釋:a為旋轉起點,b為旋轉終點,speed為旋轉速度
Quaternion.Angle(Quaternion a, Quaternion b);
獲得a和b兩個四元數之間的角度
Quaternion.FromToRotation(Vector3 a, Vector3 b);
引數解釋:a為旋轉起點,b為旋轉終點,回傳從a旋轉到b的相對旋轉量
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302583.html
標籤:其他
下一篇:初始Unity
