UnityEditorを使って2D格闘(2D Fighting game)作るときのモーション遷移図作成の半自動化に挑戦しよう<その1>

  • 1
    いいね
  • 0
    コメント

201701220440gif34.gif
空中で歩いてしまうのも、モーションとモーションをつなげるのは人力では大変だからというのもある。自動化できないものか……。

ソース公開中 Open source

https://github.com/muzudho/KifuwarabeFighter2

知ってるか、知らないかで格ゲー作成の生産性が大きく変わってきそう

UnityEditor はすごそうだ。UNITY 公式のドキュメントに説明はあるものの、

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

この 2D格闘ゲーム作成の連載だけ追いかけている人向けに 何が書いてあるのか再説明する。
201701221033a26b.png
普段見ているこの画面(上図、黄色の円)が ユニティー・エディター で、ゲームを動かすために裏で動いているの(上図、赤色の円)が ユニティー・エンジン だろう。

このうち、ユニティー・エディターを拡張できるのが UnityEditor と名付けられているスクリプトだ。

Editor フォルダー、Editor Default Resources フォルダー

Unity には特別な働きが与えられるフォルダー名が いくつかある。

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

201701221033a25b.png
Assets フォルダーの下なら場所はどこでもいいが、 Editor フォルダー と Editor Default Resources フォルダーを作っておく。

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

中には次のように書く。
201701221033a28b.png
いつもは Assembly-CSharp プロジェクトの下に C# スクリプトを書いていたと思うんだが、ユニティー・エディター用の C#スクリプトは Assembly-CSharp-Editor プロジェクトの下に自動的に分別されるようだ。

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
ばっちり 効いているようだ。

何かプログラムを書いたら、static メソッドの頭に

[MenuItem("Bye bye Earth!/Rocket1/Space2/Moon3")]

みたいなものを付けて すぐに実行できるわけだ。

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

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

Mecanim フォルダーを作る。

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

アニメーター・コントローラーを作って、パラメーターと ステートマシンを作ろう

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

        // (1)コントローラー(ファイル)を作る☆
        var controller = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath("Assets/Mecanim/StateMachineTransitions.controller");

        // (2)[parameters] に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 につながっている。smB に入ってきたときに最初に実行されるのだろうか。

(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)?

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 へ飛んでいる。

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

展望

「どのステートから」(Source)、「どのステートへ」(Destination)つなげたいかをプログラムから指定できれば作業が大幅に楽できそうだ。
201701221033a47b.png

それも名指しではなく、「条件Aに当てはまるステートから」(Source)、「条件Bに当てはまるステートへ」(Destination)つなげられるなら もっと楽できそうだし、
201701221033a48.png

そして オートでやってしまうのではなく、一旦 条件に当てはまるソースとデスティネーションのペアの一覧を出力し、適応したいものだけ選んで 実行できれば さらに融通が利くかもしれない。
201701221033a49.png

このとき面倒なのは ページネーション(1ページ目、2ページ目、次へ、前へなどの対応)だろう。

これらを 外部ツールとして用意するのではなく、Unity 上でできないものか。
クリックすると そのステートへ飛んで インスペクターが開いてほしい。

もう少し 調べものを続けることにしたい。

いつまで無料なのか、なぜ無料にしているのか分からないが、

「Unityエディター拡張入門」
http://anchan828.github.io/editor-manual/

が無料で読めるので読む。