該系列筆記基于Unity3D 5.x的版本學習帐要,部分API使用和4.x不一致怠李。
目前在Unity3D中殿衰,除了新的UGUI部分控件外朱庆,所有的物體(GameObject)都必帶有Transform組件,而Transform組件主要是控制物體在3D空間中的位置闷祥、旋轉以及縮放娱颊。
學習和掌握物體的變換是Unity3D開發(fā)者必備的基礎知識。
基礎變換
最基礎的變換就是通過腳本直接對物體的位置旋轉縮放等進行變換蜀踏。
勻速移動
我們下面實現一個勻速移動物體的效果维蒙,我們在場景中添加一個Cube物體,把下面的腳本綁定到攝像機上并把Cube拖拽賦予transfrom屬性果覆。
using UnityEngine;
using System.Collections;
public class Demo01Script : MonoBehaviour
{
????public Transform myTransform;
????void Start()
????{
????}
????void Update()
????{
????????myTransform.position = new Vector3(myTransform.position.x, myTransform.position.y + 1.0f * Time.deltaTime, myTransform.position.z);
????}
}
運行游戲颅痊,我們會發(fā)現Cube會勻速上升。我們回到編輯場景局待,對Cube進行任意的旋轉后運行游戲該Cube仍然是向上上升斑响,這是因為位置和旋轉是相互獨立的,我們直接操作位置的話程序是不會理會旋轉屬性的钳榨,更換為localPosition效果也是一致的舰罚。
根據物體方向勻速移動
我們發(fā)現如果使用上面的方法來按照物體面向的方向移動物體是不容易的,我們需要根據物體的朝向計算出x薛耻、y营罢、z這3個分量的數值再應用回物體中才行,這需要扎實的3維運算功底饼齿,不過好在Unity已經給我們提供了大量的屬性及方法饲漾,方便我們直接調用來達到我們需要的效果娇澎。
本地坐標系變量
transform.right:物體本地坐標的x軸正方向朝向坤按,1米的單位。
transform.up:物體本地坐標的y軸正方向朝向前联,1米的單位证鸥。
transform.forward:物體本地坐標的z軸正方向朝向僚楞,1米的單位。
由于我們知道了物體本地坐標的信息枉层,所以可以方便的通過這個來按照物體的朝向移動物體了泉褐,比如,下面的代碼會朝著物體的y軸正方向每秒1米的速度勻速移動:
using UnityEngine;
using System.Collections;
public class Demo01Script : MonoBehaviour
{
????public Transform myTransform;
????void Start()
????{
????}
????void Update()
????{
????????Vector3 pos = myTransform.position;
????????pos.x += myTransform.up.x * 1.0f * Time.deltaTime;
????????pos.y += myTransform.up.y * 1.0f * Time.deltaTime;
????????pos.z += myTransform.up.z * 1.0f * Time.deltaTime;
????????myTransform.position = pos;
????}
}
坐標系轉換
由于坐標系存在本地坐標系和世界坐標系兩種鸟蜡,那么就需要有方法可以對這兩種坐標系進行轉換膜赃。
transform.localToWorldMatrix:本地坐標轉世界坐標的矩陣信息。
transform.worldToLocalMatrix:世界坐標轉本地坐標的矩陣信息矩欠。
transform.TransformDirection:將方向從本地坐標轉換為世界坐標财剖,不受縮放影響。
transform.InverseTransformDirection:將方向從世界坐標轉換為本地坐標癌淮,不受縮放影響躺坟。
transform.TransformPoint:將位置從本地坐標轉換為世界坐標,受縮放影響乳蓄。
transform.InverseTransformPoint:將位置從世界坐標轉換為本地坐標咪橙,受縮放影響。
transform.TransformVector:將坐標點從本地坐標轉換為世界坐標虚倒,不受位置影響但受縮放影響美侦。
transform.InverseTransformVector:將坐標點從世界坐標轉換為本地坐標,不受位置影響但受縮放影響魂奥。
TransformPoint和TransformVector的區(qū)別
下面我們看看這兩個方法的區(qū)別菠剩,首先,我們添加一個空物體到舞臺并設置該物體的坐標為(1,1,1)耻煤,然后把Cube對象拖入該空物體成為其子項設定其坐標為(2,2,2)具壮,修改腳本如下:
using UnityEngine;
using System.Collections;
public class Demo01Script : MonoBehaviour
{
public Transform myTransform;
void Start()
{
????Vector3 pos = myTransform.TransformPoint(new Vector3(1, 1, 1));
????Debug.Log(pos);
????//(4.0, 4.0, 4.0)
????Vector3 vec = myTransform.TransformVector(new Vector3(1, 1, 1));
????Debug.Log(vec);
????//(1.0, 1.0, 1.0)
}
void Update()
{
}
}
接下來我們把空物體的尺寸縮小一半看看結果會如何:
結論
TransformPoint轉變會受物體的位置和縮放影響轉換,而TransformVector僅受物體的縮放影響轉換哈蝇。
Demo01
這里做了一個示例棺妓,具體的功能是按下指定的鍵抓取到場景中的小盒子,使其始終位于屏幕前方炮赦,按下另一個鍵將這個小盒子拋出怜跑。
下面我們看核心的實現。
using UnityEngine;
using System.Collections;
public class Demo01Script : MonoBehaviour{?
?public Transform cube;?
?void Start() { }?
?void Update()?
?{
?//抓取小盒子 if (Input.GetKey(KeyCode.Q))
?{?
?//設定小盒子的位置到屏幕前方?
?cube.transform.position = transform.TransformPoint(new Vector3(0, 0, 2));?
?//將小盒子設定為讀取對象的子對象, 保證跟隨運動
?cube.transform.SetParent(transform);?
?//去掉物理交互?
?cube.GetComponent().isKinematic = true;?
?}?
?//扔出小盒子
?if (Input.GetKey(KeyCode.E))?
?{
?if (cube.transform.parent == transform)?
?{
?//使用掉物理交互?
?cube.GetComponent().isKinematic = false;
?//解除所有子物件的綁定關系
?transform.DetachChildren();?
?//獲取方向?
?Vector3 cameraDirect = transform.TransformDirection(0, 0, 5);?
?//添加緩沖的力?
?cube.GetComponent().AddForce(cameraDirect, ForceMode.Impulse);
}
}
}
}
我們先將攝像機前的一個點轉換為世界坐標賦予給小盒子的世界坐標使其位于攝像機之前吠勘,拋出時把攝像機向前方向的一個向量轉換為世界方向賦予小盒子拋出性芬。
位移
Unity3D里提供了方便控制物體位移的屬性及方法。
本地和世界坐標
transform.position:設置和獲取物件的世界坐標看幼。
transform.localPosition:設置和獲取物件的本地坐標批旺,相對于父級的坐標。
注意诵姜,在Inspector面板中的Transform里顯示的是本地坐標汽煮。
Translate
Transform的Translate方法可以更加方便的對物體的位移進行操作,該方法有四個重載:
1publicfunction Translate(translation: Vector3, relativeTo: Space = Space.Self):void;2publicfunction Translate(x:float, y:float, z:float, relativeTo: Space = Space.Self):void;
相對于本地坐標系或世界坐標系對物體進行位移操作棚唆。
1publicfunction Translate(translation: Vector3, relativeTo: Transform):void;2publicfunction Translate(x:float, y:float, z:float, relativeTo: Transform):void;
相對于指定物體的坐標進行位移操作暇赤。
注意:如果是相對于本地坐標系,則如果向上移動就是朝向物體本身的上方移動宵凌,如果是相對于世界坐標系則是向世界的上方向移動鞋囊,如果是相對于其他物體則是向這指定的物體的上方向移動。
AnimationCurve
AnimationCurve可以用來定義自定義的動畫軌跡瞎惫,我們通過在腳本中聲明一個該類型的對象溜腐,就可以在編輯器窗口對其進行編輯译株,然后使我們的物體按照編輯的軌跡進行移動等操作。
比如我們想要得到一個物體在X軸勻速移動挺益,Y軸進行上下循環(huán)移動的時候歉糜,可以使用下面的腳本:
using UnityEngine;
using System.Collections;
public class Demo02Script : MonoBehaviour
{
public AnimationCurve myAnimationCurve;
public Transform myTransform;
void Start()
{
}
void Update()
{
myTransform.position = new Vector3(
myTransform.position.x + 1 * Time.deltaTime,
myAnimationCurve.Evaluate(Time.time * 0.5f) * 2,
myTransform.position.z);
}
}
編輯器編輯的曲線如下:
Demo02
在游戲中都會有一個最基本的需求,就是移動到指定的點望众,下面我們來實現一下這個基本的功能匪补,腳本如下:
using System;
using UnityEngine;
using System.Collections;
public class Demo02Script : MonoBehaviour
{
?public Transform myTransform;
?public Transform myTarget;?
?private bool _isArrived = true;?
?private Vector3 _origin;?
?private Vector3 _target;
?private float _speed;?
?private Action _onArrived;?
?private float _allTime;
?private float _time;
?void Start()
?{?
?MoveTo(myTarget.position, 1, () => Debug.Log("I am arrived!"));
?} void Update() { if (!_isArrived) { _time += Time.deltaTime; //判斷是否抵達終點 if (_time >= _allTime) { //校正位置 myTransform.position = _target; //標記到達和調用回調 _isArrived = true; if (_onArrived != null) { _onArrived(); } } else { //這里使用Lerp方法進行差值運算也可以得到相同的效果, 但是我們作為學習還是自己實現 //myTransform.position = Vector3.Lerp(_origin, _target, _time / _allTime); //獲取方向的單位向量 Vector3 dirction = _target - _origin; dirction.Normalize(); //朝方向運動 myTransform.Translate(dirction * Time.deltaTime); } } } ////// 移動到指定點.
//////目標點.? ? ///移動速度, 米/秒.? ? ///到達后調用的方法.? ? private void MoveTo(Vector3 targetPosition, float speed, Action onArrived)? ? {? ? ? ? _isArrived = false;? ? ? ? _origin = myTransform.position;? ? ? ? _target = targetPosition;? ? ? ? _speed = speed;? ? ? ? _onArrived = onArrived;? ? ? ? //計算總共需要花費的時間? ? ? ? _allTime = Vector3.Distance(myTransform.position, _target) / _speed;? ? ? ? //重置使用的時間? ? ? ? _time = 0;? ? }}
運行后小盒子會想指定的物體進行勻速移動,到達后會輸出“I am arrived!”的字符串烂翰。
旋轉之歐拉角
歐拉角是由3個軸的旋轉角度組成的旋轉數據夯缺,比如我們在Inspector界面的Transform中看到的就是物體本地坐標系的歐拉角:
歐拉角每個軸數字都在0-360之間,表示其旋轉的角度甘耿。
Rotate
官方提供的旋轉方法踊兜,其一共有三個重載方法:
1publicfunction Rotate(eulerAngles: Vector3, relativeTo: Space = Space.Self):void;2publicfunction Rotate(xAngle:float, yAngle:float, zAngle:float, relativeTo: Space = Space.Self):void;
指定在本地坐標系或世界坐標系下旋轉到指定的角度。
publicfunction Rotate(axis: Vector3, angle:float, relativeTo: Space = Space.Self):void;
指定在本地坐標系或世界坐標系下基于軸axis進行旋轉棵里,旋轉到angle角度润文。
RotateAround
我們先看看其參數:
publicfunction RotateAround(point: Vector3, axis: Vector3, angle:float):void;
表示我們的物體圍繞指定的點point在軸axis下旋轉angle的角度。
LookAt
可以使物體面向指定的點殿怜,我們看看其參數:
1publicvoidLookAt(Transform target, Vector3 worldUp =Vector3.up);2publicvoidLookAt(Vector3 worldPosition, Vector3 worldUp = Vector3.up);
即使我們的物體面向指定的物體或點典蝌。
旋轉之四元數
歐拉角理解和使用都相當的方便,但是在實際進行旋轉時存在萬向鎖的問題头谜,所以引入了比較抽象的四元數的概念骏掀,當然我們在Unity中只要直接使用即可,是非常方便的柱告。
這里提供一個視頻截驮,可以讓大家直觀的了解什么是萬向鎖:http://v.youku.com/v_show/id_XNzkyOTIyMTI=.html
Quaternion
在Transform中,eulerAngles屬性是使用歐拉角來表示旋轉际度,而rotation屬性則是使用四元數來表示旋轉葵袭。
四元數提供了許多的靜態(tài)方法來使我們完成特定需求的效果,點擊這里可查看幫助乖菱。
Demo03
如果我們想要實現一個效果坡锡,物體勻速旋轉到指定角度時,使用歐拉角對每個軸進行變換是相當復雜的窒所,同時如果兩個軸重合了就會出現萬向鎖的問題鹉勒,無法解決,而使用四元數則可以避免這些問題吵取,下面是實現的腳本:
using UnityEngine;
using System.Collections;
public class Demo03Script : MonoBehaviour
{
public Transform myTransform;
public Transform myTarget;
void Start()
{
}
void Update()
{
RotateToTarget();
}
private void RotateToTarget()
{
//獲取目標方向的單位向量
Vector3 dicetion = (myTarget.position - myTransform.position).normalized;
//獲取目標方向的四元數對象
Quaternion targetDicetion = Quaternion.LookRotation(dicetion);
//按照每秒 45 度的速度旋轉面向目標對象
myTransform.rotation = Quaternion.RotateTowards(myTransform.rotation, targetDicetion, 45 * Time.deltaTime);
}
}
這樣我們就可以使我們的物體勻速的轉向指定的目標對象了禽额。
縮放與位置關系
縮放
縮放比較簡單,沒有提供更多的方法皮官。
Transform.lossyScale:只讀脯倒,獲取本物體相對于世界坐標的縮放大小实辑。
Transform.localScale:設置或獲取本物體相對于父級IDE縮放大小。
位置關系
在Unity3D中藻丢,所有3D對象是按照樹形結構進行組合的徙菠,而操作物體之間的位置關系的所有API都存放在Transform對象中,下面我們看看常用的屬性及方法郁岩。
屬性
Transform.parent:設置和獲取父級對象。
Transform.root:獲取層次最高的對象缺狠。
Transform.childCount:獲取子級對象的數量问慎。
方法
Transform.Find:根據名字尋找子項。
Transform.IsChildOf:判斷是否為指定Transform對象的子項挤茄。
Transform.DetachChildren:解除所有子項如叼。
Transform.GetChild:根據索引獲取子項。
Transform.GetSiblingIndex:獲取同一級別的物體的索引穷劈。
Transform.SetAsFirstSibling:設置為同一級別的物體為第一個索引笼恰。
Transform.SetAsLastSibling:設置為同一級別的物體為最后一個索引。
Transform.SetSiblingIndex:設置同一級別的物體的索引歇终。
工程文件下載
http://pan.baidu.com/s/1sjQJ5j3
天道酬勤社证,功不唐捐!