UnityのMecanimアニメで、ムービープレーヤーのスライダーのような制御がしたい

# やりたいこと
- **ムービープレーヤーのスライダーのような制御がしたい**
    - アニメ全長の任意の位置へジャンプしたいです。
- **アニメの任意のフレームを再生したい**
    - シーケンシャルな再生でなく、任意のフレームに対してランダムにアクセスして、スクリプト制御で正逆任意の速度で再生したいです。

###### 検証環境
- Unity 2019.4.x (LTS)

# 方法

- [`Animator.Play (int stateNameHash, int layer, float normalizedTime)`](https://docs.unity3d.com/ja/current/ScriptReference/Animator.Play.html)を使います。
    - `stateNameHash`に`0`を渡すと現在のステートになります。
        - このハッシュ値は、[`Animator.GetCurrentAnimatorStateInfo (int layerIndex)`](https://docs.unity3d.com/ja/current/ScriptReference/Animator.GetCurrentAnimatorStateInfo.html)の[`AnimatorStateInfo.shortNameHash`](https://docs.unity3d.com/ja/current/ScriptReference/AnimatorStateInfo.html)で得られます。
    - `layer`に`-1`を渡すと最初に一致したステートになります。
    - `normalizedTime`には、正規化された再生位置を渡します。
        - ループするアニメの場合は、`0f`と`1f`で同じフレームが表示されます。
            - その場合は、`0.9999999f`とかにすれば最終フレームになります。
- なお…
    - [`Animator.ForceStateNormalizedTime (float normalizedTime)`](https://docs.unity3d.com/410/Documentation/ScriptReference/Animator.ForceStateNormalizedTime.html)は、非推奨です。
    - [`Animator.GetCurrentAnimatorStateInfo (int layerIndex)`](https://docs.unity3d.com/ja/current/ScriptReference/Animator.GetCurrentAnimatorStateInfo.html)で得られる[`AnimatorStateInfo.normalizedTime`](https://docs.unity3d.com/ja/current/ScriptReference/AnimatorStateInfo-normalizedTime.html)はRead-Onlyです。
    - [`Animation ["stateName"].normalizedTime`](https://docs.unity3d.com/ja/current/ScriptReference/AnimationState-normalizedTime.html)は、[<font color=black>レガシーなコンポーネント</font>](https://docs.unity3d.com/ja/current/Manual/MecanimFAQ.html)を使用します。

# 具体例
以下のコードでは、対象のスライダーとアニメーターが、コードがアタッチされたオブジェクトの子孫の何処かに存在することを前提にしています。

```cs:AnimeSlider.cs
using UnityEngine;
using UnityEngine.UI;

public class AnimeSlider : MonoBehaviour {

	private Slider slider;
	private Animator animator;

	/// <summary>初期化</summary>
	void Start () {
		slider = GetComponentInChildren<Slider> ();
		slider.onValueChanged.AddListener (OnChangeSlider);
		animator = GetComponentInChildren<Animator> ();
		animator.speed = 0; // 再生をポーズ
	}

	/// <summary>スライダが変化</summary>
	public void OnChangeSlider (float _) {
		animator.Play (0, -1, slider.normalizedValue);
	}

}
```

- これによって、スライダーの位置に応じた任意のフレームが再生されます。
- ループするアニメの場合は、スライダの左端と右端で同じフレームが表示されます。
    - 同じにしたくない場合は、`slider.normalizedValue * 0.9999999f`とかにします。
- ポーズを解除する場合は、`animator.speed = 1;`します。

# 蛇足
<details><summary>複数の同じアニメを時間差で表示</summary><div>

```cs:AnimeSlider.cs
using UnityEngine;
using UnityEngine.UI;

public class AnimeSlider : MonoBehaviour {

	private Slider slider;
	private Animator [] animators;

	/// <summary>初期化</summary>
	void Start () {
		slider = GetComponentInChildren<Slider> ();
		slider.onValueChanged.AddListener (OnChangeSlider);
		animators = GetComponentsInChildren<Animator> ();
		foreach (var animator in animators) {
			animator.speed = 0; // 再生をポーズ
		}
	}

	/// <summary>スライダが変化</summary>
	public void OnChangeSlider (float _) {
		for (var i = 0; i < animators.Length; i++) {
			var time = slider.normalizedValue - ((float) i / animators.Length);
			if (time < 0f) { time += 1f; }
			animators [i].Play (0, -1, time);
		}
	}

}
```

</div>
</details>