在Unity 中我們會希望可以平滑的移動某物件到環境的某個位置,那我們可以透過線性插植 Lerp 的方式來實現。我這邊就不太做一些Lerp 數學細節上的說明,但必須要注意到Lerp 範圍為 0~1,這是非常重要的概念之後會做簡短說明。但我今天要透過 Lerp 來實現從起點 A 到終點 B,以自己設定速度與時間完成該物件直線的移動。
今天我們會有兩個簡單得實作,由 Speed 控制 Lerp 讓物件移動的方法,以及由 Time 控制 Lerp 使物件移動的方法。
首先我們設定兩個空白物件,為起點與終點。StartPoint 與 EndPoint 。
因為在 Scene 中無法顯示空白物件,所以說這邊我們可以新增 Tag。 新增的方式在左手邊的方塊中。我們點選自己喜歡的顏色。
接著請務必記得開啟 Gizmo 來顯示我們的 Tag,若沒開啟 Scene 的Gizmo 就無法看到我們預設的Tag。
開啟後就會看到當前空白物件的位置。
接著就自行決定想要的起點與終點位置。
接著我們創建一個火車的 Prefab。隨便做做即可。設定 Prefab 後就可以先將Scene 的物件給刪除,目前不太需要。
新增一個空白物件叫做 LerpManagement ,後續新增一個文本,LerpMovement.cs。
首先我想做的功能是可以隨機切換要透過 Time 還是 Speed 來控制 Lerp 驅使物件移動的效果,這邊我先撰寫兩種不同的 IEnumerator 型別的方法,目的是為了要讓後續 StartCoroutine 可以進行呼叫。這邊若不熟悉什麼是 StartCoroutine 引入該IEnumerator 的方法,可以去參考Unity 網站的Documetation ,這邊提供連結 [https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html]
首先撰寫透過 Speed 來控制Lerp 移動物件的 IEnumerator 方法。
(StartPoint) 0 ~ 1 (EndPoint)
public IEnumerator TrainLerpBySpeed(Vector3 _from, Vector3 _to, float _speed)
float startTime = Time.time;
float disLength = Vector3.Distance(_from, _to);
float frac = 0f;
while(frac < 1.0f)
{
float subDistance = _speed * (Time.time - startTime);
frac += subDistance/disLength; // because Lerp is from 0~1, so distance/disLength
AirplaneObj.position = Vector3.Lerp(_from, _to, frac);
yield return null;
}
// distance = speed * deltaT
public IEnumerator TrainLerpBySpeed(Vector3 _from, Vector3 _to, float _speed)
{
float startTime = Time.time;
float disLength = Vector3.Distance(_from, _to);
float frac = 0f;
while(frac < 1.0f)
{
float subDistance = _speed * (Time.time - startTime);
frac += subDistance/disLength; // because Lerp is from 0~1, so distance/disLength
AirplaneObj.position = Vector3.Lerp(_from, _to, frac);
yield return null;
}
}
(time = 0) 0 ~ 1 (setTime)
public IEnumerator TrainLerpByTime(Vector3 _from, Vector3 _to, float _time)
var sub_dur = 0f;
while(sub_dur <= _time)
{
sub_dur += Time.deltaTime;
float frac = sub_dur / _time;
AirplaneObj.position = Vector3.Lerp(_from, _to, frac);
yield return null;
}
public IEnumerator TrainLerpByTime(Vector3 _from, Vector3 _to, float _time)
{
var sub_dur = 0f;
while(sub_dur <= _time)
{
sub_dur += Time.deltaTime;
float frac = sub_dur / _time;
AirplaneObj.position = Vector3.Lerp(_from, _to, frac);
yield return null;
}
}
void CheckArriveBySpeed(float s)
{
Debug.Log("Set Speed to Lerp Move!");
if(endB.position == AirplaneObj.position)
{
Debug.Log("Go to start position");
AirplaneObj.LookAt(startA.position);
StartCoroutine( TrainLerpBySpeed(endB.position, startA.position, s));
}else if(startA.position == AirplaneObj.position)
{
Debug.Log("Go to end position");
StartCoroutine( TrainLerpBySpeed(startA.position, endB.position, s));
AirplaneObj.LookAt(endB.position);
}
}
void CheckArriveByTime(float t)
{
Debug.Log("Set Time to Lerp Move!");
if(endB.position == AirplaneObj.position)
{
Debug.Log("Go to start position");
AirplaneObj.LookAt(startA.position);
StartCoroutine(TrainLerpByTime(endB.position, startA.position, t));
}else if(startA.position == AirplaneObj.position)
{
Debug.Log("Go to end position");
StartCoroutine(TrainLerpByTime(startA.position, endB.position, t));
AirplaneObj.LookAt(endB.position);
}
}
void Update()
{
if(Input.GetKeyDown("k"))
{
CheckArriveByTime(setTime);
}
if(Input.GetKeyDown("l"))
{
CheckArriveBySpeed(setSpeed);
}
}
void Start()
{
AirplaneObj.position = startA.position; // when active the system then set the new position to zero
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LerpMovement : MonoBehaviour
{
public Transform startA, endB;
public Transform AirplaneObj;
[SerializeField]public float setSpeed = 10f, setTime = 20f;
bool setting = true;
void Start()
{
AirplaneObj.position = startA.position; // when active the system then set the new position to zero
}
void Update()
{
if(Input.GetKeyDown("k"))
{
CheckArriveByTime(setTime);
}
if(Input.GetKeyDown("l"))
{
CheckArriveBySpeed(setSpeed);
}
}
void CheckArriveBySpeed(float s)
{
Debug.Log("Set Speed to Lerp Move!");
if(endB.position == AirplaneObj.position)
{
Debug.Log("Go to start position");
AirplaneObj.LookAt(startA.position);
StartCoroutine( TrainLerpBySpeed(endB.position, startA.position, s));
}else if(startA.position == AirplaneObj.position)
{
Debug.Log("Go to end position");
StartCoroutine( TrainLerpBySpeed(startA.position, endB.position, s));
AirplaneObj.LookAt(endB.position);
}
}
void CheckArriveByTime(float t)
{
Debug.Log("Set Time to Lerp Move!");
if(endB.position == AirplaneObj.position)
{
Debug.Log("Go to start position");
AirplaneObj.LookAt(startA.position);
StartCoroutine(TrainLerpByTime(endB.position, startA.position, t));
}else if(startA.position == AirplaneObj.position)
{
Debug.Log("Go to end position");
StartCoroutine(TrainLerpByTime(startA.position, endB.position, t));
AirplaneObj.LookAt(endB.position);
}
}
// distance = speed * deltaT
public IEnumerator TrainLerpBySpeed(Vector3 _from, Vector3 _to, float _speed)
{
float startTime = Time.time;
float disLength = Vector3.Distance(_from, _to);
float frac = 0f;
while(frac < 1.0f)
{
float subDistance = _speed * (Time.time - startTime);
frac += subDistance/disLength; // because Lerp is from 0~1, so distance/disLength
AirplaneObj.position = Vector3.Lerp(_from, _to, frac);
yield return null;
}
}
// speed = distance / deltaT
public IEnumerator TrainLerpByTime(Vector3 _from, Vector3 _to, float _time)
{
var sub_dur = 0f;
while(sub_dur <= _time)
{
sub_dur += Time.deltaTime;
float frac = sub_dur / _time;
AirplaneObj.position = Vector3.Lerp(_from, _to, frac);
yield return null;
}
}
}
接下來回到 Unity,將我們的起點、終點與移動的數值放入我們的 LerpMovement.cs 當中。
一開始我會將該 Train 設定 Position 到很遠的地方,先假裝沒有在 Camera 的範圍內。
執行後會看到下方 Console 顯示按下 K控制該時間決定 Lerp 移動,L 則是控制速度決定 Lerp 移動。
速度與時間我目前設定如下, Speed: 0.1, Time: 4
接著按下 L 可以看到物件移動極快。 Console 會顯示 "Set Speed to Lerp Move!” 以及 “Go to end position”。 代表說我們目前使用 Speed 控制與前往終點。
接著按下 K 可以看到物件移動極快。 Console 會顯示 "Set Time to Lerp Move!” 以及 “Go to start position”。 代表說我們目前使用 Time 控制與前往起點。
最後你可以加上 Trail Renderer Component 在 Train 上面讓整體移動更加有趣一點XDD。前幾天有深入說明,抱歉現在就不多說了。
執行結果如下
最近越來越早起了....