Unityのアニメーター・コントローラーを使ってモーションの状態遷移をプログラムから独立させようぜ

Qiitaには画像のアップロード容量上限があるようなので、古い記事を全て再構成して圧縮を計っている。

----

# こんな便利なやつがある
これは　ジャンプキック　したときの動きだ。
![201701210647gif31b.gif](https://qiita-image-store.s3.amazonaws.com/0/159417/6b51f02b-d19d-8ac7-0665-0d4a89f1e9c0.gif)
ジャンプした、キックした、といった流れが　リアルタイムに見れる。開発中に動きがおかしくなったときの調査で役に立つ。

# それは何なの？
![201701210653a49b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/af474b2a-39ea-ee15-8896-e9b295f7a4c5.png)
この　グラフみたいなやつが　アニメーター。　別に、アニメ以外のことにでも使える。

# どこにあるの？
![20170607k1b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/d7253548-5c71-5008-223d-0faf00c2f9e0.png)
アニメーション・クリップを作るときに、アニメーターが　オマケに１個　作られる。

----


# Unityのアニメーター・コントローラーを使ってモーションの状態遷移をC#スクリプトで組もうぜ

![201701220440gif34.gif](https://qiita-image-store.s3.amazonaws.com/0/159417/8a31fc0e-344b-e6cc-0079-6752ee3034ce.gif)
空中で歩いてしまっているときがある。
手作業で モーションとモーションをつなげていると、ミスというのはある。

じゃあ直そう。こういうとき、Unityのアニメーター・コントローラーが使える。
公式の記事はこれ。

「AnimatorController」unity DOCUMENT
https://docs.unity3d.com/ja/current/ScriptReference/Animations.AnimatorController.html

# まず各部名称
ここは読み飛ばして、あとで読み返してもいい。

## ユニティー・エディターと、ユニティー・エンジン
![201701221033a26b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/54d0c49d-6096-fb03-f4cb-ee027fcd1261.png)
普段、これ（上図、黄色の円）で開発していると思うが、これが **ユニティ・エディター(UnityEditor)**。

ゲーム・ビュー（上図、赤色の円）でゲームを動かしているのが **ユニティー・エンジン(UnityEngine)**　だ。

C#スクリプトを書くとき、

```C#
using UnityEditor;
using UnityEngine;
```

と冒頭に書くことがあるだろう。
ゲームとしてリリースするときは、 using UnityEditor; を使っているコードは製品に含めないだろう。

using UnityEditor; を使うときは、特別ルールが１つあって、あとで説明する。

##アニメーター
![201701221033a52c.png](https://qiita-image-store.s3.amazonaws.com/0/159417/7aff8703-69e6-1349-3a3d-6134c9393f83.png)
これ、アニメーター(Animator)。

##レイヤー
![201701221033a52e.png](https://qiita-image-store.s3.amazonaws.com/0/159417/422138ff-bec8-7bc8-d745-d8870467c420.png)
これ、レイヤーの一覧。　上図では [Base Layer] の１つしか使っていない。

##ステートマシン
![201701221033a52f.png](https://qiita-image-store.s3.amazonaws.com/0/159417/e30d6723-58fb-e7e7-23cd-ba19fac7ee4a.png)
これ、ステートマシン。線が引っ張ったりしているやつだ。
日本語で言うと　状態遷移図。

![201701230746a2.png](https://qiita-image-store.s3.amazonaws.com/0/159417/6fef9e02-0063-5b3b-1e66-c393645803e0.png)
この六角形には、さらに　ステートマシン　が入っている。
ステートマシンというのは　親子の入れ子構造にできる。

##ステート
![201701230746a3b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/dc806a72-c524-9489-1bea-03399fd87b2b.png)
この四角いのが　ステート。

###ステートのプロパティ
微妙に GUIで表示されているものと、C#スクリプトでのプロパティー名は異なる
![201701281119a91c.png](https://qiita-image-store.s3.amazonaws.com/0/159417/66300579-c5c3-afe5-1e71-cfea94456c76.png)

##トランジション
![201701230746a4b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/c699c1b5-5f3f-6329-96b1-2aaa223855ec.png)
ステート　と　ステート　をつないでいるのが、トランジション（白い矢印）だ。
ステートマシン（六角形）のやつ　ともくっつく。

###トランジションのプロパティ
微妙に GUIで表示されているものと、C#スクリプトでのプロパティー名は異なる
![201701281119a89b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/1111ce08-ae65-7ec5-2ff9-e3403fb9c886.png)


##コンディション
![201701230746a5b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/5402ec7c-30f6-7557-bc63-14c923675f15.png)
トランジションは、コンディションを持っている。[Inspector]で見れるやつだ。


## ツリー構造になっている
なんとか図にしようとすると　こんな感じ。
![201701272149a84.png](https://qiita-image-store.s3.amazonaws.com/0/159417/1f28c494-182b-44f8-a946-919610f516eb.png)

主要なのが６つ。
（１） Layer
（２） StateMachine
（３） **State** ※ステートマシンの入れ子の葉っぱに当たる
（４） **Transition** ※StateとStateを結んでいる線。 StateMachineともつながる。
（５） Condition
（６） Position

実装を見ると **チャイルドなんとか** という入れ物にラッピングされるように　ステートマシンが入っているが、基本的にステートマシン上の位置（座標とか）を入れているラッパーで包んでいるだけだ。ツリー構造に変わりはない。


# アニメーターは C#スクリプトで作ろう
今はまだ　プログラムの書き方を説明していないが、これらの部品は　C#スクリプトから作ることができる。

**多くの部品は読み取り専用だが、新規作成／追加／削除　を利用して　取っ換えることができる。**
部品によっては　全部廃棄して新規から同じものを作り直す **全取っ替え** をしなければいけないものもある。

**GUIを使って手で線を引っ張っていると　数が多くて　つらいので、C#スクリプトから作れるようになっていこう。**

じゃあ、このアニメーターは何のためのGUIかというと、リアルタイムに状態遷移を確認、不具合調査をするための監視ウィンドウぐらいに思っておこう。


# using UnityEditor; の使い方
using UnityEditor; を使うときは、特別ルールが１つある。
Assestフォルダーの下にどこでもいいので `Editor` フォルダーを作って、その下に using UnityEditor; を使う C#スクリプトを入れることだ。

![201701221033a25b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/8f7530c9-0318-ee83-bec3-532dc1a22020.png)
(上図は、**Editor** フォルダーを作った例)

Editor フォルダーは何個作ってもいい。

他に関連する特別なフォルダー名としては `Editor Default Resources` も使うことがあるかもしれない。

「特殊なフォルダー名」unity DOCUMENTATION
https://docs.unity3d.com/jp/current/Manual/SpecialFolders.html

# 使ってみる

![201701221033a27b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/9fc7650c-dc7a-add9-188d-7f6ab2dbbae6.png)
Editor フォルダーを右クリックして、[Create] - [C# Script] を選び、いいかげんな名前 Banana1 というスクリプトをとりあえず作るとする。

![201701221033a28b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/75a96f7f-8f74-b974-b597-5c5378692f1e.png)

上図左下を見てほしい。
いつもは **Assembly-CSharp** プロジェクトの下に C# スクリプトを書いていたと思うんだが、ユニティー・エディター用の C#スクリプトは **Assembly-CSharp-Editor** プロジェクトの下に自動的に分別される。

Banana1.cs には次のように書く。

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor; // 追加

public class Banana1 : MonoBehaviour {

    [MenuItem("Bye bye Earth!/Rocket1/Space2/Moon3")]
    static void GoToTheMoon()
    {
        Debug.Log("Moon is great!");
    }

}
```

![201701221033a29b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/0edf3421-de82-f86c-b3e6-cd6de258b2b8.png)
このように　メニューは追加できる。

![201701221033a30b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/dd81982f-b0c1-031e-fcff-c777c3c9bf5d.png)
ばっちり　効いているようだ。このテスト手法はよく使う。

Unityで知っておきたい基本操作は別記事にまとめた：http://qiita.com/muzudho1/items/8fa08d0d2b4c205df49c


#じゃあ、UNITY公式のサンプル・プログラムを実行してみよう。

（再掲）
https://docs.unity3d.com/ja/current/ScriptReference/Animations.AnimatorController.html

###Mecanim フォルダーを作る。
![201701221033a31b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/aa0e08fa-9209-8e9c-6fce-bfe828ff5490.png)
まず、Assets フォルダーの下に Mecanim というフォルダーを作る。
Mecanim という名前のフォルダーに意味はない。サンプルでの練習が終わったら消してもいい。

###コードをいじろう
一度に実行すると　変化についていけないので、コメントアウトしながら　少しずつ実行していく。
![201701221033a32.png](https://qiita-image-store.s3.amazonaws.com/0/159417/b920525e-76ff-2de8-1960-31cb5775b82a.png)
サンプル・プログラムは　変数名や　コメント等を　改造してしまった。

```C#
        // （１）サンプルのコントローラー（ファイル）を見に行く☆
        var controller = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath("Assets/Mecanim/StateMachineTransitions.controller");

        // （２）アニメーター・ビューの[parameters] に C#スクリプトから ４つ追加する。
        controller.AddParameter("TransitionNow", AnimatorControllerParameterType.Trigger);
        controller.AddParameter("Reset", AnimatorControllerParameterType.Trigger);
        controller.AddParameter("GotoB1", AnimatorControllerParameterType.Trigger);
        controller.AddParameter("GotoC", AnimatorControllerParameterType.Trigger);

        // （３）ステートマシン（六角形のやつ）を３つ追加する。
        var rootStateMachine = controller.layers[0].stateMachine;
        var smA = rootStateMachine.AddStateMachine("smA"); // stateMachineA
        var smB = rootStateMachine.AddStateMachine("smB"); // stateMachineB
        var smC = smB.AddStateMachine("smC"); // stateMachineC
```

この C#スクリプトを　さっきのように Editor フォルダーの下に入れ、[MyMenu] - [Create Controller] から実行すると、

![201701221033a33c.png](https://qiita-image-store.s3.amazonaws.com/0/159417/32a25477-a245-5442-36b8-ff00f88ed4e6.png)

Assets/Mecanim フォルダーの下に StateMachineTransitions.controller （アニメーター・コントローラー）ファイルが作られ　**……（１）**

画面左端の方には　TransitionNow、Reset、といったトリガーが並び、**……（２）**

そのとなりの方眼紙のところには　smA、smB　といった六角形の箱（これをステートマシンと呼ぶ）ができている。なお、smC は smB の中に入っている。**……（３）**

![201701221033a34b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/a633440a-c683-9e8f-9a85-d6e19e47dacf.png)
ほらね。

[Entry] [Any State] [Exit] は　どこにでも入っている。
[(Up) Base Layer] は、上に戻るぐらいの意味だ。

##ステートを追加しよう
![201701221033a35b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/ec0a09f3-0259-d0de-3dfe-09d08c24be96.png)

```C#
        // （４）ステート（長方形のやつ）を５つ追加する。
        var stateA1 = smA.AddState("stateA1"); // （５）（６）
        var stateB1 = smB.AddState("stateB1"); // （７）
        var stateB2 = smB.AddState("stateB2"); // （７）
        smC.AddState("stateC1"); // （８）
        var stateC2 = smC.AddState("stateC2"); // （８） don’t add an entry transition, should entry to state by default
```

ステート（長方形）を追加する。あとで線を引っ張るものは、var を使って　変数に取り置きしている。

（５）
![201701221033a36b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/56101fbc-4ba7-6416-f390-11b43e7b325e.png)
最初に作ったのが stateA1 なので、smA の方に　暗いオレンジ色の矢印が伸びているようだ。

（６）
![201701221033a37b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/81408cc6-138b-ecbc-7a40-9c8164af3203.png)
左上のパンくずリストに書いてあるとおり、smA の中に stateA1 ができている。また、１つ上の階層の Base Layer の Entry から伸びてきた線が、Entry から入ってきて stateA1 につながっている。

オレンジ色の線は、最初に実行されるステートにつながっている。明示的に設定していないときは暗い色の線になっている。

（７）
![201701221033a38b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/606aae52-f31d-beaf-93f9-2241cc08d238.png)
smB の中もにぎやかだ。 stateB1、stateB2 が新しく作られている。 smC は　その前の（３）で作ったものだ。

ここでは　Entry から stateB1 につながっている。

（８）
![201701221033a39b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/201b576d-c16a-a65d-7272-bff314d8667c.png)
smC の中には stateC1 と stateC2 が作られている。

#白い矢印の線（トランジション）を付けよう
![201701221033a40b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/0603f35c-4f2f-2074-0b72-956947d56329.png)
よく分からないコードもある。

```C#
        // 以下、トランジション（白い矢印の線）を追加する。

        // （５）stateA1 から Exit へのトランジション
        var exitTransition = stateA1.AddExitTransition(); // stateA1 は Exit につなげる。
        
        exitTransition.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "TransitionNow"); // 条件にトリガーを追加する
        exitTransition.duration = 0; // duration は 0 に。

        // （６）（これは分からない） stateA1 と Any State がつながっていないようだが？
        var resetTransition = smA.AddAnyStateTransition(stateA1);
        resetTransition.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "Reset");
        resetTransition.duration = 0;
```

![201701221033a41b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/26bf9400-f8ed-9d7d-ebd1-b48ebc2959a6.png)
stageA1 が Exit につながったのは分かるんだが　……（５）
別に smA の Any State も、上の階層の Any State も、stateA1 にはつながっていない。……（６）？

C#スクリプトで Any State から線を引く方法はあるんだが、ひとまず公式のサンプル通り　先に進もう。

#条件にトリガーも付けよう
![201701221033a42b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/312be4c9-1b59-e0bd-2621-086cb74dd78e.png)
さっき説明し忘れたんだが……。

```C#
        // （７）stateB1 は Entry につなげる。
        var transitionB1 = smB.AddEntryTransition(stateB1);
        transitionB1.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "GotoB1"); // 条件にトリガーを追加する。
        // （８）stateB2 も Entry につなげる。
        smB.AddEntryTransition(stateB2);
        // （９）stateC2 は Entry につなげる方法ではなく、デフォルトとして設定する。
        smC.defaultState = stateC2;
        // （１０）stateC2 は Exit につなげる。
        var exitTransitionC2 = stateC2.AddExitTransition();
        exitTransitionC2.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "TransitionNow"); // 条件にトリガーを追加する。
        exitTransitionC2.duration = 0;
```

線（トランジション）をクリックして　Inspector ビューを見ると
![201701221033a43b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/2a3ae846-4751-f734-a586-92cda2470ea7.png)
Conditions に GotoB1 トリガーが設定されていることが分かる。……（７）
また、smC に線が伸びているが、これは stateC2 がデフォルト設定になっているので、そこへ伸びていっているものだ。

![201701221033a44b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/7064c731-a223-0ffd-2255-6609f66dfac6.png)
上の階層から Entry を経由して stateC2 に線がつながっている。


#ステートマシン（六角形）同士をつなげよう
![201701221033a45b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/85a62953-867f-fd54-49c2-94105287fe81.png)
ステートを直接つなぐ以外にも、ステートマシンをつなぐ方法がある。

この場合、遷移元のステートマシンの Exit と、遷移先のステートマシンの Enter がつながる。

![201701221033a46b.png](https://qiita-image-store.s3.amazonaws.com/0/159417/5a113315-1465-2e0a-647e-172699669a87.png)
smB ではなく、smC へ飛んでいる。

一部　はしょったが、サンプル・プログラムは　ここまでだ。
これで　線をつなげるのは　C#スクリプトから　できることが分かった。

----

# 予備知識




```
Project
  |
  +-- ゲームオブジェクト
      ~~~~~~~~~~~~~~~~
        |
        +-- スプライト・レンダラー
        |
        +-- アニメーター
```

ゲーム画面上に置いてあるものを　ゲームオブジェクト　と呼ぶ。
画像（≒スプライト）を貼り付けたり、モーション遷移（≒アニメーター）を付けたりする。

```
Project
  |
  +-- ゲームオブジェクト
        |
        +-- スプライト・レンダラー
        |
        +-- アニメーター

Assets/Resources/Sprites
  |
  +-- スプライト
      ~~~~~~~~~
```

ゲームオブジェクトとは別にスプライトがあるわけだが、このスプライトは単に画像ぐらいと思っておけばいい

```
Project
  |
  +-- ゲームオブジェクト
        |
        +-- スプライト・レンダラー
        |
        +-- アニメーター

Assets/Resources/Sprites ※別にこのフォルダーでなくてもいい
  |
  +-- スプライト
        |
        +-- タイル状にスライスした画像の１枚１枚
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

スプライトは、スプライト・エディターを使って　１枚の画像を　タイル状にスライスしたことを覚えておくこともできる。

```
Project
  |
  +-- ゲームオブジェクト
        |
        +-- スプライト・レンダラー
        |   ~~~~~~~~~~~~~~~~~~~~
        |
        +-- アニメーター

Assets/Resources/Sprites ※別にこのフォルダーでなくてもいい
  |
  +-- スプライト
        |
        +-- タイル状にスライスした画像の１枚１枚
```

スプライト・レンダラーは、スプライトを描画するものだ。

```
Project
  |
  +-- ゲームオブジェクト
        |
        +-- スプライト・レンダラー
        |    |
        |    +-- スプライト
        |        ~~~~~~~~~
        |
        +-- アニメーター

Assets/Resources/Sprites
  |
  +-- スプライト
      ~~~~~~~~~
        |
        +-- タイル状にスライスした画像の１枚１枚
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

スプライト・レンダラーに　スプライトを指定すれば、
タイル状にスライスした画像を参照できるようになり、
アニメーションの高速描画を担当させることができる。

```
Project
  |
  +-- ゲームオブジェクト
        |
        +-- スプライト・レンダラー
        |
        +-- アニメーター

Assets/Resources/AnimationClips ※別にこのフォルダーでなくてもいい
  |
  +-- アニメーション
      ~~~~~~~~~~~~~

Assets/Resources/Sprites
  |
  +-- スプライト
        |
        +-- タイル状にスライスした画像の１枚１枚
```

どのような　アニメーションにするかは、アニメーション　というファイル に覚えさせる。
ドープシート（≒タイムライン）に　スライスした画像を並べてモーション・アニメーションを作ることができる。

```
Project
  |
  +-- ゲームオブジェクト
        |
        +-- スプライト・レンダラー
        |    |
        |    +-- タイル状にスライスした画像の１枚１枚
        |        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        |
        +-- アニメーター

Assets/Resources/AnimationClipsい
  |
  +-- アニメーション
        |
        +-- タイル状にスライスした画像の１枚１枚
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Assets/Resources/Sprites
  |
  +-- スプライト
        |
        +-- タイル状にスライスした画像の１枚１枚
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

ここらへん全部、スライスした画像を間にして　依存しあいあっているわけだ。


----

次の記事に続く。

「Unityのアニメーター・コントローラーはどこまでC#スクリプトで作成できるかやってみた」
http://qiita.com/muzudho1/items/f6d8a10d057307057b7f

「Unityで状態遷移を組むときに使うアニメーター・コントローラーを作成するC#スクリプトのソースコード」
http://qiita.com/muzudho1/items/d2a76d79b8a6b8437897