2017年3月27日 星期一

Unity 3D 移動公式 心得筆記

大家好,最近才領悟到心得筆記可以放在部落格,一來在自己找歷史資料時方便,二來也許整理的心得會有需要的人,也可方便取用,就這樣。

這邊若要活用移動方式,會建議學習向量相關的知識,會比較了解為何可以達到這種效果,但如果不學,看懂套用方式直接套用也可以,就看本身自己的需求如何了。

接下來介紹一下在Unity中,移動可分為 移動模式移動方式
移動方式又有分非物理影響物理影響的移動方式,因為我的專案目前還不需要用到,物理影響相關的公式,所以我這邊就不再特別介紹,等我需要的時候,再一併整理上來吧。

移動模式:移動中的路徑變化
  • Vector3.Lerp:根據來源地與目的地,兩點畫一直線做直線位移運動,以時間軸控制。
  • Vector3.Slerp:根據來源地與目的地,兩點畫一曲線做曲線位移運動,曲度由來源目標的物件方向與目的地的位置做角度偏移量決定。
  • Vector3.MoveTowards:與Vector3.Lerp做一樣的直線運動,控制項是以移動速度做為控制。
  • Vector3.SmoothDamp:與Vector3.Lerp做一樣的直線運動,但加入了時間軸控制與最大移動速度限制控制。
移動方式:移動時,是屬於跳耀方式還是連續性的位移
  • Transform.position:瞬間移動到定點位置 (較不受碰撞影響)。
  • Transform.Translate:平滑移動 (可受碰撞影響),會讓移動看起來更順,但需要位移時間。
可看需求搭配組合:
  • Transform.Position定點跳耀的移動方式需搭配對應的移動模式
  • Transform.Translate:本身就有支援直線位移的功能,所以只有非直線位移的方式,才需要額外撰寫移動模式。
接下來介紹實際使用的方式與內容。



Vector3.Lerp使用時機:
做直線運動時,並須要在特定時間內到達定點,或是想要在移動的過程中做漸快漸慢,因為是直線運動所以比較常跟transform.position做搭配使用。

函式宣告:
Vector3.Lerp(from : Vector3, to : Vector3, t : float) : Vector3
from:起始座標
to:目的座標
t:以時間軸控制位移[0, ... , 1],1秒鐘到達目的,0.5秒代表中間位置,所以自己要換算所需時間。
return:當下座標

使用範例:
public class example : MonoBehaviour {
 public Transform start;
 public Transform end;
 void Update() {
  transform.position = Vector3.Lerp(start.position, end.position, Time.time);
 }
}



Vector3.MoveTowards使用時機:
與Lerp一樣做直線運動,不一樣的地方是Lerp是控制時間軸,而MoveTowards是控制移動速度,當要做等速度移動或是保持某個速度移動時適用。

函式宣告:
Vector3.MoveTowards (current : Vector3, target : Vector3, maxDistanceDelta : float) : Vector3
current :起始座標
target :目標座標
maxDistanceDelta :保證移動時,不會超過我們設定的速度,正值就是追向目標,負值就是遠離目標。
return:當下座標

使用範例:
public class example : MonoBehaviour {
 public Transform start;
 public Transform end;
 void Update() {
  transform.position = Vector3.MoveTowards(start.position, end.position, 30.0f);
 }
}



Vector3.Slerp使用時機:從一個座標方向到一個目標座標畫一個弧線,使用時間軸控制位移座標,可以用於曲線移動到目標,比如火球術,或光球追跡彈。

函式宣告:
Vector3.Slerp (from : Vector3, to : Vector3, t : float) : Vector3
from:起始座標
to:目的座標
t:以時間軸控制位移[0, ... , 1],1秒鐘到達目的,0.5秒代表中間位置,所以自己要換算所需時間。
return:當下座標

使用範例:
public class example : MonoBehaviour {
 public Transform sunrise;
 public Transform sunset;
 void Update() {
  Vector3 center = sunrise.position + sunset.position * 0.5F;
  center -= new Vector3(0, 1, 0);
  Vector3 riseRelCenter = sunrise.position - center;
  Vector3 setRelCenter = sunset.position - center;
  transform.position = Vector3.Slerp(riseRelCenter, setRelCenter, Time.time);
  transform.position += center;
 }
}



Vector3.SmoothDamp使用時機:這是Lerp與MoveTowards的功能總合版本,會以漸快的方式達到一個速度後,保持速度,當目標不再移動時,會已漸慢速度移動到目標位置,感覺就像開車停車一樣,保持較為平滑變化較小的位移狀態,最常見的用法用於攝影機跟隨目標。

函式宣告:
Vector3.SmoothDamp(current : Vector3, target : Vector3, ref currentVelocity : Vector3, smoothTime : float, maxSpeed : float = Mathf.Infinity, deltaTime : float = Time.deltaTime) : Vector3
current:目前所在位置
target:目的地位置
currentVelocity:當前的速度 是返回值
smoothTime:達到目標的時間設定
maxSpeed:最大移動速度限制
deltaTime:上次調用此函數的時間 默認Time.deltaTime
return:回傳 更新的座標位置

使用範例:
public class example : MonoBehaviour {
 public Transform target;
 public float smoothTime = 0.3F;
 private Vector3 velocity = Vector3.zero;
 void Update() {
  Vector3 targetPosition = target.TransformPoint(new Vector3(0, 5, -10));
  transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);
 }
}



Transform.position使用時機:當不需要碰撞,而且希望更迅速更省效能的方式移動到我們想移動到的位置時,可以選擇此方法。

物件宣告:
Transform.position : Vector3
無法直接針對裡面的xyz做運算,當需要改變裡面的數值時可透過向量運算或是new的方式重新賦予座標位置。

Vector3本身可以表示成座標位置也可表示成速度向量,這邊就要看讀者怎麼解讀使用他了,
但在這邊的公式參數幾乎都是以座標位置為主 所以要當成速度向量的話,就需要做公式轉換寫成:
目的位置 = 來源位置 * 速度向量
這樣子就可以讓他在移動時不會到達目標點就停住了。

使用範例:
public class example : MonoBehaviour {
        //目標位置
 public Transform target;
 void Update() {
                //計算兩物體的距離
                float dis = Vector3.Distance(transform.position, traget.position);
                //保持距離,若遠離則追逐目標。
                if(dis >= 5) {
                        //持續注視目標
                        transform.LookAt(target);
                        //持續往目標前進
                        transform.position += transform.forward*Time.deltaTime * 1;
  }
 }
}



Transform.Translate使用時機:平滑移動 (可受碰撞影響),會讓移動看起來更順,因為在移動的過程,自己會去運算補間位移的過程,但移動到位置的時間會比較慢,而且在運算中比較容易耗效能。

函式宣告:
    以自身位置為基準:
    Transform.Translate (translation : Vector3, relativeTo : Space = Space.Self) : void
    Transform.Translate (x : float, y : float, z : float, relativeTo : Space = Space.Self) : void
    translation: 給予移動方向與速度
    relativeTo: 此速度向量是依照哪個座標系 Space.Self or Space.World

    依據目標位置 給予速度向量,設定移動距離
    Transform.Translate (translation : Vector3, relativeTo : Transform) : void
    Transform.Translate (x : float, y : float, z : float, relativeTo : Transform) : void
    translation: 速度向量
    relativeTo: 目標位置

使用範例:
public class example : MonoBehaviour {
 void Update() {
  transform.Translate(Vector3.forward * Time.deltaTime);
  transform.Translate(Vector3.up * Time.deltaTime, Space.World);
 }
}

Transform.Translate本身配合 Transform.Rotate 就可以使用控制角度的方式前進移動,移動會依照目前物件面向的方向一值前進,而我們可以透過Rotate改變他的移動角度。

使用範例:
public class example : MonoBehaviour {
 void Update() {
  transform.Translate(Vector3.forward * Time.deltaTime);
  transform.Rotate(Vector3.up * Time.deltaTime * 50.0f);
 }
}



飛機移動方式

public class OwnFighter : MonoBehaviour
{
    /// <summary>移動速度</summary>
    public float fMoveSpeed = 1.0f;
    /// <summary>旋轉速度</summary>
    public float fRotateSpeed = 5.0f;

    void Update()
    {
        //上下旋轉
        float l_fH = Input.GetAxis("Mouse X") * fRotateSpeed;
        transform.Rotate(Vector3.up * Time.deltaTime * l_fH * 100);
        //左右旋轉
        float l_fV = Input.GetAxis("Mouse Y") * fRotateSpeed;
        transform.Rotate(Vector3.left * Time.deltaTime * l_fV * 100);
        //往前移動
        transform.Translate(Vector3.forward * Time.deltaTime * fMoveSpeed);
    }
}


補充:

有鋼體的話可以用鋼體設定速度的方式讓他自行移動。
l_obj.GetComponent<Rigidbody>().velocity = transform.TransformDirection(Vector3.forward * 10);



以下也開放留言,可以問一些問題,可以再相互討論精進。

沒有留言:

張貼留言