【Unity】Mathf.LerpとInverseLerpの覚書

使うときは頻繁に使うし使わないと全然使わないのでなかなか覚えられない Mathf.Leap と InverseLLeap の挙動のメモです。

Mathf.Lerp

リファレンスの説明は以下の通り。

// [a, b] の範囲内で補間する値 value を生成する線形パラメーター t を計算します
float t = Mathf.Leap(float a, float b, float value)
// a: 開始値
// b: 終了値
// value: 開始と終了の間の値

value = 0 ~ 1.0 の範囲の割合(%)を指定すると、対応す値 t が取得できる。具体的には以下の通り。

a b value t memo
0 100.0 -0.1 0 ★最小値より下は最小値
0 100.0 0 0 0~100までの間で0%の値=0
0 100.0 0.5 50.0 0~100までの間で50%の値=50
0 100.0 1.0 100.0 0~100までの間で100%の値=100
0 100.0 1.1 100.0 ★最大値より上は最大値

必ずしも a < b でなくてもよい。a > b でも正常動作する。値が増えると減少するみたいな場合 a > b にして数値を入れ替えると b - t せずに済む。

Mathf.InverseLeap

リファレンスの冒頭の説明が Mathf.Leap と同じ…

// [a, b] の範囲内で補間する値 value を生成する線形パラメーター t を計算します
float t = Mathf.InverseLerp(float a, float b, float value)
// a: 開始値
// b: 終了値
// value: 開始と終了の間の値

value = 0 ~ 100 の範囲の具体値を指定すると対応する割合 t が取れる。Inverse なので value と t が入れ替わる。具体的には以下の通り。

a b value t memo
0 100.0 -1 0 ★最小値より下は最小値
0 100.0 0 0 0~100までの間で0は0%
0 100.0 50 0.5 0~100までの間で50は50%
0 100.0 100 1.0 0~100までの間で100は100%
0 100.0 101 1.0 ★最大値より上は最大値

こちらも必ずしも a < b でなくてもよい。a > b でも正常動作する。逆数を取りたいときは 1.0 - t よりも a < b に入れ替えたほうがよい。

範囲を変更する

上記のメソッドを組み合わせて 0 ~ 100 の範囲で 25 だった数値の割合を維持しながら、0 ~ 1000 の範囲でいくつか(=この場合250)を取得するメソッドを作成したいと思います。

public class MathfUtil
{
    // 割合を維持しながら範家を変更する
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static float ChangeRange(float curMin, float curMax, float value,
            float newMin, float newMax)
    {
        if (value >= curMax)
        {
            return newMax;
        }

        if (value <= curMin)
        {
            return newMin;
        }
        return Mathf.Lerp(newMin, newMax, Mathf.InverseLerp(curMin, curMax, value));
    }
}

// 使い方

// 0 - 100 で 25 を 0 - 1000 の範囲だと 250
float t1 = MathfUtil.ChangeRange(0, 100, 25, 0, 1000);
// t1 = 250

// 0 - 255 で 180 を 0 - 100 の範囲だと 70.588
float t2 = MathfUtil.ChangeRange(0, 256, 180, 0, 100);
// t2 = 70.5882339

ちなみに「計算量が多い」場合で「範囲が毎回固定」の場合、あらかじめ係数を以下のように

float factor = (curMax - curMin) / (newMax - newMin);
// 例えば0~4096を0~255に直した場合
// factor = 0.062255859375

先に計算しておいて

if (value >= curMax)
{
    return newMax;
}
if (value <= curMin)
{
    return newMin;
}
int newValue = (int)Mathf.Round(value * factor);

としたほうが処理速度が100倍以上早くなるのであらかじめ係数が分からない場合などで使用すると幸せになれます(割り算は遅いです)

中身の処理

Mathf に実装されているメソッドの実際の実装は以下の通りです。

// Mathf.cs

public static float Lerp(float a, float b, float t)
{
    return a + (b - a) * Clamp01(t);
}

public static float InverseLerp(float a, float b, float value)
{
    if (a != b)
    {
        return Clamp01((value - a) / (b - a));
    }
    return 0f;
}

public static float LerpUnclamped(float a, float b, float t)
{
    return a + (b - a) * t;
}

public static float Clamp01(float value)
{
    if (value < 0f)
    {
        return 0f;
    }
    if (value > 1f)
    {
        return 1f;
    }
    return value;
}