ひとりでのアプリ開発 - fineの備忘録 -

ひとりでアプリ開発をするなかで起こったことや学んだことを書き溜めていきます

Unity - Vector3.Lerp、SlerpとVector3.LerpUnclamped、SlerpUnclamped

初めに

 本記事では、二点間を線形に移動させる Lerp、球面線形に移動させる Slerp について説明します。また、Lerpと LerpUnclamped、Slerp と SlerpUnclamped の違いについてまとめます。



Lerp

 Vector3.Lerp は2点間を線形補間、すなわち2点間の内分点を取得する関数になります。

public static Vector3 Lerp (Vector3 a, Vector3 b, float t);
//aが始点, bが終点, tが0から1までのパラメータ

 Lerp は a + (b - a) * t で与えられえる Vector3 を返す関数になります。直線のベクトル方程式や内分点の公式が分かると、式の意味が理解できます。

パラメータ t の値 返り値
t=0 a
t=0.2 2点a, bを0.2:0.8に内分する点
t=0.5 (a+b)/2 (a と b の中点)
t=0.7 2点a, bを0.7:0.3に内分する点
t=1 b

 Lerp を使うと、2点間を直線移動させることができます。

 下のスクリプトは、Lerp を使った2つのオブジェクト間を直線移動させるためのスクリプトです。

using UnityEngine;

public class LerpMovement : MonoBehaviour
{
    public Transform startMarker; // 始点オブジェクト
    public Transform endMarker; // 終点オブジェクト
    public float speed = 1.0f; // 移動速度
    private float startTime; // 開始時間
    private float journeyLength; // 移動距離

    void Start()
    {
        startTime = Time.time;
        journeyLength = Vector3.Distance(startMarker.position, endMarker.position);
    }

    void Update()
    {
        float distCovered = (Time.time - startTime) * speed;  //開始から現在までの移動距離
        float fracJourney = distCovered / journeyLength;  //始点から終点までの距離を1としたときの始点と現在地の距離の割合
        transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fracJourney);  //線形移動
    }
}

(実行)
youtu.be

Slerp

 Vector3.Slerp は2点間を球面線形補間する関数になります。

 ※Slerp の S はおそらく Sphere:球 の S

public static Vector3 Slerp (Vector3 a, Vector3 b, float t);
//aが始点, bが終点, tが0から1までのパラメータ
//Lerpと同じ

 Slerp を使うと、2点の中点を中心とする球面上を移動させることができます。

 下のスクリプトは、Slerpを使った2つのオブジェクト間を球面上移動させるためのスクリプトです。(先ほどのLerp のスクリプトの Vector3.Lerp を Vector3.Slerp に変更しただけ)

using UnityEngine;

public class SlerpMovement : MonoBehaviour
{
    public Transform startMarker; // 始点オブジェクト
    public Transform endMarker; // 終点オブジェクト
    public float speed = 1.0f; // 移動速度
    private float startTime; // 開始時間
    private float journeyLength; // 移動距離

    void Start()
    {
        startTime = Time.time;
        journeyLength = Vector3.Distance(startMarker.position, endMarker.position);
    }

    void Update()
    {
        float distCovered = (Time.time - startTime) * speed;
        float fracJourney = distCovered / journeyLength;
        transform.position = Vector3.Slerp(startMarker.position, endMarker.position, fracJourney);  //Slerp
    }
}

(実行)
youtu.be

LerpUnclamped、SlerpUnclamped

 Lerp、Slerp はパラメータの範囲が0から1に制限されていました。LerpUnclamped、SlerpUnclampedはパラメータの範囲に制限がありません。

 LerpUnclamped はパラメータに制限がないため、2点を両端とする線分上ではなく、2点を通る直線上にある 2 つのベクトル間を補間します。SlerpUnclamped もパラメータに制限がないため、2点を直径とする球面上を回り続けます。

public static Vector3 LerpUnclamped (Vector3 a, Vector3 b, float t);
public static Vector3 SlerpUnclamped (Vector3 a, Vector3 b, float t);
//ともに引数、返り値がLerpと同じ

(LerpUnclamped を使って、2点を通る直線上を動かすスクリプト
 ※先ほどの Lerp の部分を LerpUnclamped に変更しただけ

using UnityEngine;

public class LerpMovement : MonoBehaviour
{
    public Transform startMarker; // 始点オブジェクト
    public Transform endMarker; // 終点オブジェクト
    public float speed = 1.0f; // 移動速度
    private float startTime; // 開始時間
    private float journeyLength; // 移動距離

    void Start()
    {
        startTime = Time.time;
        journeyLength = Vector3.Distance(startMarker.position, endMarker.position);
    }

    void Update()
    {
        float distCovered = (Time.time - startTime) * speed;
        float fracJourney = distCovered / journeyLength;
        transform.position = Vector3.LerpUnclamped(startMarker.position, endMarker.position, fracJourney);
    }
}

(実行)
youtu.be

(SlerpUnclamped を使って、2点を直径とする球面上を動かすスクリプト
 ※先ほどの Slerp の部分を SlerpUnclamped に変更しただけ

using UnityEngine;

public class SLerpMovement : MonoBehaviour
{
    public Transform startMarker; // 始点オブジェクト
    public Transform endMarker; // 終点オブジェクト
    public float speed = 1.0f; // 移動速度
    private float startTime; // 開始時間
    private float journeyLength; // 移動距離

    void Start()
    {
        startTime = Time.time;
        journeyLength = Vector3.Distance(startMarker.position, endMarker.position);
    }

    void Update()
    {
        float distCovered = (Time.time - startTime) * speed;
        float fracJourney = distCovered / journeyLength;
        transform.position = Vector3.SlerpUnclamped(startMarker.position, endMarker.position, fracJourney);
    }
}

(実行)
youtu.be

最後に

  • Lerp は線形補間
  • Slerp は球面線形補間
  • Lerp、Slerp のパラメータは0から1まで
  • Unclamped がついているものはパラメータに制限がない