LoginSignup
8
4

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-06-08

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


こんな便利なやつがある

これは ジャンプキック したときの動きだ。
201701210647gif31b.gif
ジャンプした、キックした、といった流れが リアルタイムに見れる。開発中に動きがおかしくなったときの調査で役に立つ。

それは何なの?

201701210653a49b.png
この グラフみたいなやつが アニメーター。 別に、アニメ以外のことにでも使える。

どこにあるの?

20170607k1b.png
アニメーション・クリップを作るときに、アニメーターが オマケに1個 作られる。


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

201701220440gif34.gif
空中で歩いてしまっているときがある。
手作業で モーションとモーションをつなげていると、ミスというのはある。

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

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

まず各部名称

ここは読み飛ばして、あとで読み返してもいい。

ユニティー・エディターと、ユニティー・エンジン

201701221033a26b.png
普段、これ(上図、黄色の円)で開発していると思うが、これが ユニティ・エディター(UnityEditor)

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

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

using UnityEditor;
using UnityEngine;

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

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

アニメーター

201701221033a52c.png
これ、アニメーター(Animator)。

レイヤー

201701221033a52e.png
これ、レイヤーの一覧。 上図では [Base Layer] の1つしか使っていない。

ステートマシン

201701221033a52f.png
これ、ステートマシン。線が引っ張ったりしているやつだ。
日本語で言うと 状態遷移図。

201701230746a2.png
この六角形には、さらに ステートマシン が入っている。
ステートマシンというのは 親子の入れ子構造にできる。

ステート

201701230746a3b.png
この四角いのが ステート。

ステートのプロパティ

微妙に GUIで表示されているものと、C#スクリプトでのプロパティー名は異なる
201701281119a91c.png

トランジション

201701230746a4b.png
ステート と ステート をつないでいるのが、トランジション(白い矢印)だ。
ステートマシン(六角形)のやつ ともくっつく。

トランジションのプロパティ

微妙に GUIで表示されているものと、C#スクリプトでのプロパティー名は異なる
201701281119a89b.png

コンディション

201701230746a5b.png
トランジションは、コンディションを持っている。[Inspector]で見れるやつだ。

ツリー構造になっている

なんとか図にしようとすると こんな感じ。
201701272149a84.png

主要なのが6つ。
(1) Layer
(2) StateMachine
(3) State ※ステートマシンの入れ子の葉っぱに当たる
(4) Transition ※StateとStateを結んでいる線。 StateMachineともつながる。
(5) Condition
(6) Position

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

アニメーターは C#スクリプトで作ろう

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

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

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

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

using UnityEditor; の使い方

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

201701221033a25b.png
(上図は、Editor フォルダーを作った例)

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

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

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

使ってみる

201701221033a27b.png
Editor フォルダーを右クリックして、[Create] - [C# Script] を選び、いいかげんな名前 Banana1 というスクリプトをとりあえず作るとする。

201701221033a28b.png

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

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

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
このように メニューは追加できる。

201701221033a30b.png
ばっちり 効いているようだ。このテスト手法はよく使う。

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

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

(再掲)
https://docs.unity3d.com/ja/current/ScriptReference/Animations.AnimatorController.html

Mecanim フォルダーを作る。

201701221033a31b.png
まず、Assets フォルダーの下に Mecanim というフォルダーを作る。
Mecanim という名前のフォルダーに意味はない。サンプルでの練習が終わったら消してもいい。

コードをいじろう

一度に実行すると 変化についていけないので、コメントアウトしながら 少しずつ実行していく。
201701221033a32.png
サンプル・プログラムは 変数名や コメント等を 改造してしまった。

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

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

        // (3)ステートマシン(六角形のやつ)を3つ追加する。
        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

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

画面左端の方には TransitionNow、Reset、といったトリガーが並び、……(2)

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

201701221033a34b.png
ほらね。

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

ステートを追加しよう

201701221033a35b.png

        // (4)ステート(長方形のやつ)を5つ追加する。
        var stateA1 = smA.AddState("stateA1"); // (5)(6)
        var stateB1 = smB.AddState("stateB1"); // (7)
        var stateB2 = smB.AddState("stateB2"); // (7)
        smC.AddState("stateC1"); // (8)
        var stateC2 = smC.AddState("stateC2"); // (8) don’t add an entry transition, should entry to state by default

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

(5)
201701221033a36b.png
最初に作ったのが stateA1 なので、smA の方に 暗いオレンジ色の矢印が伸びているようだ。

(6)
201701221033a37b.png
左上のパンくずリストに書いてあるとおり、smA の中に stateA1 ができている。また、1つ上の階層の Base Layer の Entry から伸びてきた線が、Entry から入ってきて stateA1 につながっている。

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

(7)
201701221033a38b.png
smB の中もにぎやかだ。 stateB1、stateB2 が新しく作られている。 smC は その前の(3)で作ったものだ。

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

(8)
201701221033a39b.png
smC の中には stateC1 と stateC2 が作られている。

白い矢印の線(トランジション)を付けよう

201701221033a40b.png
よく分からないコードもある。

        // 以下、トランジション(白い矢印の線)を追加する。

        // (5)stateA1 から Exit へのトランジション
        var exitTransition = stateA1.AddExitTransition(); // stateA1 は Exit につなげる。

        exitTransition.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "TransitionNow"); // 条件にトリガーを追加する
        exitTransition.duration = 0; // duration は 0 に。

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

201701221033a41b.png
stageA1 が Exit につながったのは分かるんだが ……(5)
別に smA の Any State も、上の階層の Any State も、stateA1 にはつながっていない。……(6)?

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

条件にトリガーも付けよう

201701221033a42b.png
さっき説明し忘れたんだが……。

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

線(トランジション)をクリックして Inspector ビューを見ると
201701221033a43b.png
Conditions に GotoB1 トリガーが設定されていることが分かる。……(7)
また、smC に線が伸びているが、これは stateC2 がデフォルト設定になっているので、そこへ伸びていっているものだ。

201701221033a44b.png
上の階層から Entry を経由して stateC2 に線がつながっている。

ステートマシン(六角形)同士をつなげよう

201701221033a45b.png
ステートを直接つなぐ以外にも、ステートマシンをつなぐ方法がある。

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

201701221033a46b.png
smB ではなく、smC へ飛んでいる。

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


予備知識

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


次の記事に続く。

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

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

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4