【Unity】イージング関数の定義と、任意のイージング関数を返す関数を定義する

イージング処理をしたいときに、Tween系assetを入れるのも大袈裟なので
イージング関数を定義したクラスを記載しておきます。
コピペしてそのまま使えるようにしました。

また、イージング関数を返す関数を定義してあります。
これを用いることで関数の引数に、イージングの種類を加えることで、
**イージング関数の差し替えが簡単に**できるようになります。

#### 参考
関数の実装については以下のサイトを参考にしてます。

https://easings.net/ja

#### コード
```c#:Easing.cs

using System;
using UnityEngine;

public class Easing
{
    public enum Ease
    {
        // 緩急 弱 ↑
        // 一次関数
        Linear,

        // Sine
        InSine,
        OutSine,
        InOutSine,

        // 二次関数
        InQuad,
        OutQuad,
        InOutQuad,

        // 三次関数
        InCubic,
        OutCubic,
        InOutCubic,

        // 四次関数
        InQuart,
        OutQuart,
        InOutQuart,

        // 五次関数
        InQuint,
        OutQuint,
        InOutQuint,

        // 指数関数
        InExpo,
        OutExpo,
        InOutExpo,

        // 円形関数
        InCirc,
        OutCirc,
        InOutCirc,

        // 1度のみ振動
        InBack,
        OutBack,
        InOutBack,

        // 弾性
        InElastic,
        OutElastic,
        InOutElastic,
    
        // バウンド
        InBounce,
        OutBounce,
        InOutBounce
        // 緩急 強 ↓
    }

    // Func<in T, out TResult>(T arg);
    // イージング関数を返す関数
    public static Func<float, float> GetEasingMethod(Ease ease)
    {
        switch(ease)
        {
            case Ease.Linear:
                return Linear;
            
            case Ease.InSine:
                return EaseInSine;
            case Ease.OutSine:
                return EaseOutSine;
            case Ease.InOutSine:
                return EaseInOutSine;

            case Ease.InQuad:
                return EaseInQuad;
            case Ease.OutQuad:
                return EaseOutQuad;
            case Ease.InOutQuad:
                return EaseInOutQuad;
            
            case Ease.InCubic:
                return EaseInCubic;
            case Ease.OutCubic:
                return EasesOutCubic;
            case Ease.InOutCubic:
                return EasesInOutCubic;
            
            case Ease.InQuart:
                return EaseInQuart;
            case Ease.OutQuart:
                return EaseOutQuart;
            case Ease.InOutQuart:
                return EaseInOutQuart;

            case Ease.InQuint:
                return EaseInQuint;
            case Ease.OutQuint:
                return EaseOutQuint;
            case Ease.InOutQuint:
                return EaseInOutQuint;
            
            case Ease.InExpo:
                return EaseInExpo;
            case Ease.OutExpo:
                return EaseOutExpo;
            case Ease.InOutExpo:
                return EaseInOutExpo;
            
            case Ease.InCirc:
                return EaseInCirc;
            case Ease.OutCirc:
                return EaseOutCirc;
            case Ease.InOutCirc:
                return EaseInOutCirc;
            
            case Ease.InBack:
                return EaseInBack;
            case Ease.OutBack:
                return EaseOutBack;
            case Ease.InOutBack:
                return EaseInOutBack;
            
            case Ease.InElastic:
                return EaseInElastic;
            case Ease.OutElastic:
                return EaseOutElastic;
            case Ease.InOutElastic:
                return EaseInOutElastic;
            
            case Ease.InBounce:
                return EaseInBounce;
            case Ease.OutBounce:
                return EaseOutBounce;
            case Ease.InOutBounce:
                return EaseInOutBounce;

            default:
                return Linear;
        }
    }

    public static float Linear(float x)
    {
        return x;
    }

    public static float EaseInSine(float x)
    {
        return 1.0f - Mathf.Cos((x * Mathf.PI) / 2.0f);
    }

    public static float EaseOutSine(float x)
    {
        return Mathf.Sin((x * Mathf.PI) / 2.0f);
    }

    public static float EaseInOutSine(float x)
    {
        return - (Mathf.Cos(Mathf.PI * x) - 1.0f) / 2.0f;
    }

    public static float EaseInQuad(float x)
    {
        return x * x;
    }

    public static float EaseOutQuad(float x)
    {
        return 1.0f - (1.0f - x) * (1.0f - x);
    }

    public static float EaseInOutQuad(float x)
    {
        return x < 0.5f ? (2.0f * x * x) : (1.0f - Mathf.Pow(-2.0f * x + 2.0f, 2.0f)/ 2.0f);
    }

    public static float EaseInCubic(float x)
    {
        return x * x * x;
    }

    public static float EasesOutCubic(float x)
    {
        return 1.0f - Mathf.Pow(1.0f - x, 3.0f);
    }

    public static float EasesInOutCubic(float x)
    {
        return x < 0.5f ? (4.0f * x * x * x) : (1.0f - Mathf.Pow(-2.0f * x + 2.0f , 3.0f) / 2.0f);
    }

    public static float EaseInQuart(float x)
    {
        return  x * x * x * x;
    }

    public static float EaseOutQuart(float x)
    {
        return 1.0f - Mathf.Pow(1.0f - x, 4.0f);
    }

    public static float EaseInOutQuart(float x)
    {
        return x < 0.5f ? (8.0f * x * x * x * x) : (1.0f - Mathf.Pow(-2.0f * x + 2.0f, 4.0f) / 2.0f);
    }

    public static float EaseInQuint(float x)
    {
        return x * x * x * x * x;
    }

    public static float EaseOutQuint(float x)
    {
        return 1.0f - Mathf.Pow(1.0f - x, 5.0f);
    }

    public static float EaseInOutQuint(float x)
    {
        return x < 0.5f ? (16.0f * x * x * x * x * x ) : (1.0f - Mathf.Pow(-2.0f * x + 2.0f, 5.0f) / 2.0f);
    }

    public static float EaseInExpo(float x)
    {
        return x == 0.0f ? (0.0f) : Mathf.Pow(2.0f, 10.0f * x - 10.0f);
    }

    public static float EaseOutExpo(float x)
    {
        return x == 1.0f ? 1.0f : 1.0f - Mathf.Pow(2.0f, -10.0f * x);
    }

    public static float EaseInOutExpo(float x)
    {
        return x == 0.0f ? 0.0f 
        : x == 1.0f ? 1.0f
        : x < 0.5f ? Mathf.Pow(2.0f, 20.0f * x - 10.0f) / 2.0f
        : (2.0f - Mathf.Pow(2.0f, -20.0f * x + 10.0f)) / 2.0f;
    }

    public static float EaseInCirc(float x)
    {
        return 1.0f - Mathf.Sqrt(1.0f - Mathf.Pow(x, 2.0f));
    }

    public static float EaseOutCirc(float x)
    {
        return Mathf.Sqrt(1.0f - Mathf.Pow(x - 1.0f, 2.0f));
    }

    public static float EaseInOutCirc(float x)
    {
        return x < 0.5f
        ? (1 - Mathf.Sqrt(1.0f - Mathf.Pow(2.0f * x, 2.0f))) / 2.0f
        : (Mathf.Sqrt(1.0f - Mathf.Pow(-2.0f * x + 2.0f, 2.0f)) + 1.0f) / 2.0f;
    }

    public static float EaseInBack(float x)
    {
        float c1 = 1.70158f;
        float c3 = c1 + 1.0f;
        return c3 * x * x * x - c1 * x * x;
    }

    public static float EaseOutBack(float x)
    {
        float c1 = 1.70158f;
        float c3 = c1 + 1.0f;
        return 1.0f + c3 * Mathf.Pow(x - 1.0f, 3.0f) + c1 * Mathf.Pow(x - 1.0f, 2.0f);
    }

    public static float EaseInOutBack(float x)
    {
        float c1 = 1.70158f;
        float c2 = c1 * 1.525f;
        return x < 0.5f 
        ? (Mathf.Pow(2.0f * x, 2.0f) * ((c2 + 1.0f) * 2.0f * x - c2)) / 2.0f
        : (Mathf.Pow(2.0f * x - 2.0f, 2.0f) * ((c2 + 1.0f) * (x * 2.0f - 2.0f) + c2) + 2.0f) / 2.0f;
    }

    public static float EaseInElastic(float x)
    {
        float c4 = (2.0f * Mathf.PI) / 3.0f;
        return x == 0.0f
        ? 0.0f
        : x == 1.0f
        ? 1.0f
        : -Mathf.Pow(2.0f, 10.0f * x - 10.0f) * Mathf.Sin((x * 10.0f - 10.75f) * c4);
    }

    public static float EaseOutElastic(float x)
    {
        float c4 = (2.0f * Mathf.PI) / 3.0f;
        return x == 0.0f
        ? 0.0f
        : x == 1.0f
        ? 1.0f
        : Mathf.Pow(2.0f, -10.0f * x) * Mathf.Sin((x * 10.0f - 0.75f) * c4) + 1.0f;
    }

    public static float EaseInOutElastic(float x)
    {
        float c5 = (2.0f * Mathf.PI) / 4.5f;
        return x == 0.0f
        ? 0.0f
        : x == 1.0f
        ? 1.0f
        : x < 0.5f
        ? -(Mathf.Pow(2.0f,  20.0f * x - 10.0f) * Mathf.Sin((20.0f * x - 11.125f) * c5)) / 2.0f
        :  (Mathf.Pow(2.0f, -20.0f * x + 10.0f) * Mathf.Sin((20.0f * x - 11.125f) * c5)) / 2.0f + 1.0f;
    }

    public static float EaseInBounce(float x)
    {
        return 1.0f - EaseOutBounce(1.0f - x);
    }

    public static float EaseOutBounce(float x)
    {
        float a = 7.5625f;
        float b = 2.75f;
        if (x < 1.0f / b) 
        {
            return a * x * x;
        }
        else if (x < 2.0f / b) 
        {
            float c = (x - 1.5f / b);
            return a * c * c + 0.75f;
        }
        else if (x < 2.5 / b) 
        {
            float c = (x - 2.25f / b);
            return a * c * c + 0.9375f;
        }
        else 
        {
            float c = (x - 2.625f / b);
            return a * c * c + 0.984375f;
        }
    }

    public static float EaseInOutBounce(float x)
    {
        return x < 0.5f
        ? (1.0f - EaseOutBounce(1.0f - 2.0f * x)) / 2.0f
        : (1.0f + EaseOutBounce(2.0f * x - 1.0f)) / 2.0f;
    }
}

```

#### 使い方
イージング関数の引数には、0～1の値を代入すると、
対応するイージングの値を返してくれます。

```HowToUse.cs
// EaseOutSineの0.5の値を取得する
float val = Easing.EaseOutSine(0.5f);
```

GetEasingMethod()関数は、引数のenumに対応するイージング関数を返してくれます。
関数の引数に、Easing.Easeのenumを設定すると、
イージング関数の差し替えが簡単になります。

毎フレーム取得すると重いと思うので、
最初のフレームでのみ関数を取得、
それ以降のフレームでは、取得した関数を利用する。
のようなコードを書いた方が良いと思われます。

```c#:HowToUse.cs
// EaseOutSine関数を取得する
var EaseOutSine = Easing.GetEasingMethod(Easing.Ease.OutSine);

// EaseOutSineの0.5の値を取得する
float val = EaseOutSine(0.5f);
```

例：Transformに値を入れて、移動させる関数と使用例

```c#:MoveObjectSample.cs

// 指定したTransformの座標を更新する
// 引数 Transform, 目的値の座標、何秒で動かすか、イージングの種類、移動先が絶対座標かどうか
public IEnumerator Move(Transform transform, Vector3 destinationPos, float seconds, Easing.Ease easing, bool absolute)
{
    // イージング関数の取得
    var Ease = Easing.GetEasingMethod(easing);

    // 現在点と移動先の設定
    Vector3 staPos = transform.localPosition;
    Vector3 endPos = absolute ? destinationPos : staPos + destinationPos;
    // 初期地点と目標地点の差
    Vector3 difPos = endPos - staPos; 

    // N秒かけて移動させる
    float e = 0;
    while(true)
    {
        yield return null;
        e += Time.deltaTime / seconds;
        if(e >= 1.0f)
        {
            transform.localPosition = endPos;
            break;
        }
        Vector3 nextPos = staPos + Ease(e) * difPos;
        transform.localPosition = nextPos;
    }       
}

void Start()
{
    // 引数のEnumを変えるだけで、イージング関数の差し替えができる

    // EaseOutCubicで、相対座標で1,2,3の位置に1秒かけて移動させる
    StartCoroutine(
        Move(this.transform, new Vector3(1, 2, 3), 1.0f, Easing.Ease.OutCubic, false)
    );

    // EaseOutQuadで、相対座標で1,2,3の位置に1秒かけて移動させる
    StartCoroutine(
        Move(this.transform, new Vector3(1, 2, 3), 1.0f, Easing.Ease.OutQuad, false)
    );
}
```


#### 備考
Bounce関数のみ参考サイトでの定義が謎だったので、
以下のサイトから式を引用しています。

https://www.anarchive-beta.com/entry/2022/04/17/190000