非エンジニア向けに簡易的なアニメーションビューア作ってみた

この記事は[サムザップ #2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/sumzap2)の12/15の記事です。
前日は @Gaku_Ishii さんの[【Unity】Androidのファイル作成パフォーマンスはディレクトリにある既存ファイル数の影響を受けるのか？](https://qiita.com/Gaku_Ishii/private/71a4a9c1b7681ebe4019)でした。

# はじめに

タイトルの通りですが非エンジニア向けに超絶簡易的なアニメーションビューア機能を作ってみました。
本来ならUnityのAnimatorウィンドウとAnimationウィンドウを使えば事足りる機能ですが、本機能は以下の点を重視して手早く作ったものになります。ご留意ください。

- 非エンジニア向け
- インスペクタで完結する
- アニメーションの指定と再生・停止ができる

## 環境
Unity 2018.4.27f1

# demo

こんな感じでLayersとStateをプルダウンから選択して再生・一時停止・再開・停止が可能です
![画面収録 2020-12-14 10.46.30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218179/d3a4e3c8-0a55-f12e-d8dc-0e9ff2436712.gif)
Unityちゃんのアニメーションチェックシーン使っているため若干ややこしい見た目になってますが、インスペクタ内からアニメーションの指定と再生・停止・一時停止・再開ができているのが確認出来るかと思います。
解像度が低くて分かりづらいかもですが、ボタンは左からPlay、Pause、Resume、Stopとなっています。

動画には入っていませんが、AnimationEventsの設定有無も確認できます。

# 実装

難しいことは一切やっていません。ただただAnimator側の情報を取って表示しているだけです。
細かい制御などは端折って記載しています。

```csharp
// ...省略...

/// <summary>
/// CustomAnimator参照
/// </summary>
private CustomAnimator _customAnimator;

/// <summary>
/// レイヤー名を格納する配列
/// </summary>
private string[] _layerNames = new string[] { };

/// <summary>
/// ステート名を格納するディクショナリ
/// </summary>
private Dictionary<int, string[]> _stateNamesDict = new Dictionary<int, string[]>();

private int _layerIndex;

private CustomAnimator CustomAnimator
{
    get
    {
        if (_customAnimator == null)
            _customAnimator = target as CustomAnimator;
        return _customAnimator;
    }
}

public override void OnInspectorGUI()
{
    // ...省略...
    
    EditorGUILayout.LabelField("デバッグ再生");
    _layerIndex = EditorGUILayout.Popup("Layers", _layerIndex, _layerNames);

    var stateNames = _stateNamesDict.ContainsKey(_layerIndex) ? _stateNamesDict[_layerIndex] : new string[] { };
    _stateIndex = EditorGUILayout.Popup("States", _stateIndex, stateNames);
    _stateName = stateNames.Length > 0 ? stateNames[_stateIndex] : string.Empty;
    
    using (new EditorGUILayout.HorizontalScope())
    {
        if (GUILayout.Button("Play"))
        {
            var fullPathHash = Animator.StringToHash(string.Join(".", _animator.GetLayerName(_layerIndex), _stateName));
            CustomAnimator.Play(fullPathHash, _layerIndex, _normalizedTime, () =>
            {
                Debug.Log("Complete");
            });
        }
        if (GUILayout.Button("Pause"))
        {
            CustomAnimator.Pause();
        }
        if (GUILayout.Button("Resume"))
        {
            CustomAnimator.Resume();
        }
        if (GUILayout.Button("Stop"))
        {
            CustomAnimator.Stop();
        }
    }

    // ...省略...
}

// ...省略...
```

実行時にAnimatorControllerのLayersとStatesの拾い方がわからなくてググりました。
以下方法で拾えることがわかって勉強になりました。

```csharp
// UnityEditorのAnimatorControllerでキャスト
var controller = _animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
for (var i = 0; i < controller.layers.Length; i++)
{
    var layer = controller.layers[i];
    Debug.Log(layer.name); // レイヤー名

    var states = layer.stateMachine.states;
    foreach (var state in states)
    {
        Debug.Log(state.state.name); // ステート名
    }
}             
```

以上になります。
明日は @IwakamiYuki さんの記事です。