見出し画像

TCC SbS 紐解きドキュメント Move編

これはTCCのStep by Stepシーンの解説ドキュメントです。
本書を読むことで、TCCに付属しているサンプルの中身を紐解けるようになります。

多分。


最初に

Project_TCCのダウンロード場所
https://github.com/unity3d-jp/Project_TCC

本書の役割

TCCとはTinyCharacterControllerの略称で、これはUnityのVisualScriptingをより簡単で便利に扱う為のコンポーネント郡の総称です。

その名前が示す通り、キャラクターを主体としてゲームを開発していくことを目的として開発され、キャラクターアーティストがゲームを作りやすい設計になっているのが特徴です。

とは言え、ゲームを作る以上「画」のことだけを考えているわけにも行きません。
また、普段からゲームをする人ならば「あのゲームのあの機能」や「あのゲームのあの動作」といったイメージが先行してしまい、最初から高みを目指してしまいがちです。

結論から言うと、TCCとVisualScriptingを使えば、およそ大体の「あのゲームのあの機能」や「あの動作」は実装可能です。
しかし、最初から高度なシステムを組もうとしても、一体どこから手を付けて良いのか、そもそもどんな機能を使えば良いのか、一歩目の足の置き場にさえ悩んでしまう事でしょう。

そこで用意されたのがStep by Stepコンテンツです。

このドキュメントはTCCと、TCCを使う上で必要になるVisualScriptingの機能解説が主となっているため、Unityの基本機能や使い方についてはあまり深掘りしません。
例えば「先ずキャラクターを配置します」といった説明は、本書では行われない事をご留意ください。StepByStepなのに。

本書はあくまでTCCに収録されているサンプルシーンの解説であり、シーンを開いたときどこを見ればよいか、どのような機能がどう組まれているかを知るための手助けをする物とお考えください。


Step by Stepについて

Step by Stepには沢山のサンプルシーンが同梱されています。
まずは何が入っているのか、TCCでどのようなことが出来るのかを知るために、01.Moveから最後まで全てのシーンを開いて再生してみましょう。

収録されているシーンは全て、プレイヤーの入力によって何かしらの動作が発生する様になっているので、キーボード(WASD)やゲームパッドを使って動かしてみてください。

恐らく、いくつかのシーンを遊んでみると「あ、これが作りたい」となるシーンが出てくると思います。
私もキャラクターアーティストなので、あなたの気持ちはよく理解出来ます。

そう、つまり、あなたがまず知りたいのは、そのシーンを自分のキャラクターやアバター、すなわち「うちの子」に差し替えて遊ぶ為の方法である事は火を見るより明らかなのです。

そもそも、このシステムに手を出す人の多くは自分のキャラクターを動かしたいという欲求があるはずなので、私は先ずそれを叶える手段を提供したいと思います。

それは僅か3ステップで完了してしまうほど簡単ですが、置き換えたいキャラクターのリグがHumanoidでなければ動作しない事にだけ注意してください。


00.キャラクターの差し替え

それでは最初のシーンである01.Moveの01-01_Walkというシーンを開き、実際にキャラクターを置き換えてみましょう。

①サンプルシーンには必ずPlayerというGameObjectが存在し、その下に何らかのキャラクターPrefabが配置されています。

②このキャラクターPrefabを削除して、同じ場所に自分のキャラクターPrefabを配置します。

③Prefabを配置したらPlayerのAnimatorコンポーネントのAvatarを配置したキャラクターの物に差し替えます。

これでキャラクターの差し替えは完了です。
やりましたね。

念のため、置き換えたPrefabにAnimatorがある場合は非アクティブにしておきましょう。
削除しても構いません。
非アクティブになっていない場合は、少々混乱した動作をする可能性があります。

さぁ、これで今後開かれる全てのシーンの中で貴方の興味を惹く物があったとき、何時でも自分のキャラクターに置き換える事が出来るようになりました。

勿論、差し替えたシーンを起点として様々な機能を足していく形でゲームを作り進める事も可能です。
とは言え、サンプルには本当に様々なシーンが収録されており、一部のシーンは上記の方法だけでは完璧な置き換えが出来ない場合もあります。

例えば武器を持っていたりすると、武器の配置を調整する必要がありますし、銃を構えるシーンでは銃の位置に加えてグリップやヒジのIK位置などの調整が必要になります。

ま、それはその時考えるとしましょう。

それでは最初のシーンから順にシーンを見ていきましょうか。
本書を脇に置き、全てのシーンを理解したあとは、自由にゲームを作れる世界があなたを待っています。


01.Walk

おっと、このシーンはもう開きましたか?
それどころか、キャラクターの差し替えまで終わっているって?
準備は完璧ですね。

それでは、このシーンがどのような仕組みでキャラクターを動かしているかを見ていきましょう。

最初に見るところはヒエラルキーのPlayerオブジェクトです。

PlayerオブジェクトのInspectorにはいくつかのコンポーネントが付属していますね。
このうちTCCの機能はCharacterSettingsCharacterBrainMoveControlの3つだけです。
CharacterControllerやAnimator、ScriptMachineといったコンポーネントはTCCではなくUnityの標準機能で構成されています。

本来であれば全てのコンポーネントを紹介していくところですが、このドキュメントはせっかちさんの為に色々すっ飛ばして…もとい、「とりあえず先に知りたい部分」を紹介するスタイルで行きます。

というわけで、Playerの持つScriptMachineというコンポーネントを見てみましょう。
これがVisualScriptingを行う為のコンポーネントで、EditGraphボタンを押すとGraphViewが開きます。

そうするとこのようなエディターが立ち上がると思いますが、これがVisualScriptingを行う為のエディタ、GraphViewになります。

何やら2つのノードが複数のフレームで囲われていますね。

VisualScriptingの基本操作についてはより詳しく紹介している記事や動画があると思うので、ここではすっ飛ばします。
なに、それほど難しいものでは有りませんよ。

マウスの中ボタンドラッグでビューをスクロール出来る事と、このSubgraphというノードをダブルクリックすると中身が見れる事だけ知っていれば大丈夫です。

…いえ、待ってください

私としたことが、三度の食事よりも大切なことを伝え忘れていました。
PreferenceのVisualScriptingという項目にControlSchemeという設定がありますが、ここはAlternateにしておきましょう。

これによってホイール操作でGraphの拡大縮小が可能になります

さて、話を戻して…
VisualScriptingは実行中にも動作の具合をリアルタイムで見ることが出来るので、画面のどこかにドッキングしておくのもよいでしょう。

この状態で実行しても、GraphViewには何も変化が起きません。
まずは右側のSubgraph、Animation Updateをダブルクリックして中身を見てみましょう。

すると何やらカラフルな画面になりました。
色の付いた付箋は、そのノードが何を行っているのかを注釈しているだけで、重要なのは中央の駆動部です。

「Animation Update」というグループが青くなり、何やらエネルギーが転送されているような動きをします。
これは自動で毎フレーム処理を行うOnUpdateが使われているため、プレイヤーが何も入力しなくても動作している事を表しています。

因みにSubgraphから抜けて最初の画面に戻る時は、GraphView左上にあるPlayerと書かれた場所をクリックすれば戻れます。
ゲームを実行中でも何時でも戻ることが出来ます。

それではもう一方の「Move」の中を見てみましょう。
OnInputSystemEventVector2(OnHold)は何も反応していません。

これはPlayerが持つPlayerInputコンポーネントで設定したInputActionをトリガーにして動作するノードですので、このグラフの場合はPlayerInputのMoveというActionがOnHold、つまり押し続けられている間のみ動作するようになっています。

Playerオブジェクトが持つPlayerInputコンポーネントを見てみましょう。
Actionsというところをダブルクリックすると、ActionMapエディタが起動します。

ActionMapエディタにはMoveの項目があり、WASDやスティック入力などが割り当てられています。
見ての通りですが、つまるところキーコンフィグですね。

OnInputSystemEventVector2で指定されているMoveの正体はこれです。
LeftStick(Gamepad)やWASDがOnHold=押されている間、MoveControlのMoveに入力された情報が渡されます。

実行中にWASDまたはゲームパッドの左スティックを操作してみましょう。

キャラクターが移動し、GraphViewの「Move」グループもノードが青くなり、数値が転送されているのが分かります。

この転送先のノードはPlayerが持っているMoveControlコンポーネントであり、これがTCCの機能です。

MoveControlはシンプルに移動機能を与えるコンポーネントです。
沢山パラメータがありますが、まぁ大体見れば分かりますよね?

重要なのはこのコンポーネントをPlayerが持っているという事です。
VisualScriptingではこのコンポーネントが持っているパラメータに直接数値を流し込む事で様々な動作を行うことができます。

GraphViewの空いている場所で右クリックし、Codebase/TinyCharacterController/Control/の中にMoveControlがあります。

MoveControlを選ぶと更に沢山のノードリストが出てきますが、これらは全てMoveControlを介して取得したり付与することのできるパラメータになります。

この中のGetCurrentSpeedはMoveContorlが発行する「現在の移動速度」を取得できるノードです。

つまりこの「アニメーションの更新」グループにあるノードは、OnUpdate(毎フレーム更新)でGetCurrentSpeed(現在の速度)を取得し、取得した値をPlayerが持つAnimatorのSpeedに流し込んでいるというわけです。

PlayerのAnimatorを見ると、SpeedというParametersがありますね。

これにより、IdleからWalkへの遷移条件を達成し、Speedが0.1を上回ったら歩きモーションを再生させ、0.1を下回ったらIdleに戻る様に設計されているため、移動に合わせて歩行モーションを再生し、止まったらアイドリングモーションが再生される様になっているのです。


まとめ

この様に、VisualScriptingはPlayerInputやAnimatorなど、Unityが持つ様々なコンポーネントにダイレクトにアクセスし、それらの数値を取得したり与えたりすることで、Playerを始めとする様々なゲームオブジェクトをインタラクティブに動かすことが出来るのです。

このシーンの仕組みから分る事でもありますが、本来キャラクターの動作、つまりMoveControlで与えられる移動量と、キャラクターが行うアニメーションは必ずしも相互依存関係になっていません。
移動速度に応じてアニメーションを変化させる事で「歩いて移動しているように見せている」のです。

ゲーム開発ではこれらを全て自分で繋いで作る必要があります。
VisualScriptingで大分緩和されたとは言え、まだまだ難しいゲーム開発を更にまろやかにする為に用意されたのがTCCであり、なんならもっと手間を省く為に、自分が作りたいゲームに最も近い動作テンプレートとして用意されたのがStep by Stepのもう一つの目的、というわけなのです。


01-02.Walk and Run

01.Walkで歩く仕組みとVisualScriptingの構造はなんとなく分かりましたか?
ご心配なく。なんとなく把握できていれば結構です。

簡単に復習すると、Playerに与えられたコンポーネントが持っているパラメータをVisualScriptingにより直接弄って動作を変化させる、というものでした。

さて、Walkでは「待機」と「歩き」しかありませんでしたが、走るボタン…そうですね、例えばShiftを押してる間、MoveControlのMoveSpeedを上昇させるようにすれば、走れるようになるのでは?

01-02 Walk and Runを開いてみましょう。

VisualScriptingのGraphViewには前回同様にMoveと、新しくSprintが追加されています。
おや、何やら注釈がありますね。

なるほど。そういう事ならMoveの方から見ていきましょう。

確かに、AnimationUpdateがMoveの中にひとまとめになっています。
これは動作を確認する際もラクチンでよいですね。

実はこのSubgraphという機能は、ペイントツールなどでいうところのレイヤーフォルダやプリコンポーズに近い物…といえば伝わるでしょうか?

一枚のGraphView上ですべての機能を作ってしまうと、ノードだらけになって視認性が悪く、メンテナンスも難しくなってしまうのを防ぐ為に、最初から機能単位でひとまとめにする形で使用していました。

Actionグループの中にあるMoveは移動系をまとめた物で、Sprintは今回新たに作った走る処理の為のSubgraphです。

ちなみに、Subgraph自体は右クリックして「sgra」と入力すれば一番上に出てきてくれます。
VisualScriptingの検索機能は中々優秀で、モノグサな自分にとても良くしてくれています。

それでは、今回の目玉であるSprintを見てみましょうか。

う~ん、思った以上に単純ですね。

SelectOnFlowにつながっている「A」はImputActionのSprintがOnHold状態の時、MoveSpeedが「4」になるように設定されており、「B」は同じSprintがOnReleased、つまり入力が切れた時、MoveSpeedが「1.2」に戻るよう設定されています。

ということは、PlayerInputにSprintという項目が追加されているに違い有りません。
早速見に行って、とっちめてやりましょう。

やはりありました。
Moveに加えてしれっとSprintが追加されていますね。
中身は…Shift[Keyboard]とRightShoulder[Gamepad]となっています。

走る仕組みが見えてきましたね。
最後に調べるべき場所はAnimator、手がかりは走る速度として設定された「4」という数字です。

一見すると何も変化がないように見えますが、実はこのMoveというStateはBrendTreeという複数のAnimationClipを指定条件でクロスフェードさせる物に置き換わっています。
ダブルクリックするとBrendTreeの設定画面に移ります。

見つけました、「4」です!
このBrendTreeはSpeedが2から4に近づくにつれて徐々に走るモーションがブレンドされる仕組みになっているようです。

一旦整理しましょう。

まず、Idle状態からMoveに遷移する条件はSpeedが0.1以上になっています。
これは停止状態から0.1以上の速度が与えられた時、Moveステートに遷移することを意味します。
前回Walkでやったところですね。

PlayerInputのMoveで与えられる初期速度は最大で1.2なので、この遷移は達成出来ますが、4未満なので、BrendTreeでRunに遷移することは出来ません。

Sprintが入力されると、入力期間中はPlayerの移動速度が1.2から4に変化します。
これによって移動速度が最大4まで上昇するので、しきい値2から4に加速されるに従って、スムーズに走りモーションに遷移されるというカラクリです。

謎は全て解けましたね、警部。

しかし気になるのはこの「2」という数字です。
初期最大速度が1.2なら、ここは1.2でも良いのではないでしょうか。
一体犯人は何故ここを2にしたのでしょう…?


結論からいうと、ここは1.2でも全く問題なく動作します。
では何故2なのかと言うと、Sprintが入力されて、MoveControlコンポーネントで設定されたAcceleratorの速度でMoveSpeedが上昇する時、1.2から2まではモーションの遷移が行われません。
つまり、差分の「0.8」は入力バッファという事になるわけです。
(厳密にはアニメーションの切り替えバッファ、というべきでしょうか)

仮にここを1.2にすると、Sprintを入力した瞬間から走りモーションへのブレンドが始まります。
それはそれで何も問題ないのですが、もし1にしてしまった場合、オーバーした0.2分は走りモーションがブレンドされた状態になり、モーションデザイナーは少し違和感を感じるかもしれません。

どちらにせよ、移動速度が4まで上がったら走っているので、初期移動速度より上の設定になってさえいれば、よほどShiftを連打しない限り、ほとんど気にならない部分と言ってしまってよいでしょう。


まとめ

キャラクターを走らせる仕組みは思った以上に単純でしたね。
特に新しいノードを使うこともなく、MoveControlの中だけで完結してしまいました。

こうしてみるとMoveControlコンポーネントを眺める意味が出てくる感じがします。
ここに表示されているパラメータであれば外からアクセス出来る、と考えると、作りたい挙動への道筋がうっすら見えてくるのではないでしょうか。

今回は速くしましたが、逆に遅くすることでAim中の速度低下や水たまり、沼など歩きにくい場所を作るのにも役立ちそうです。

MoveControl以外で言えばAnimatorで使われたBrendTreeが新要素だった、くらいでしょうか。
これも使いこなすと非常に奥深く複雑なシステムなので、今後のStep by Stepで再び取り上げる事もあるでしょう。
今は、そういう機能がある事を知っていただければ大丈夫です。

さぁ、次はジャンプを実装しますよ。


01-02.Move and Jump

歩く、走るができる様になったら、やはりジャンプさせたくなるのは人の常。
TCCにはキャラクターをジャンプさせるためのコンポーネントが用意されています。

まずはシーンを開いてみましょう。
UC-01-03_Move_and_Jumpです。

ヒエラルキーでPlayerを選択して見ると、何やら見慣れないコンポーネントが幾つか付いていますね。
新たに登場したのはGroundCheckJumpControlGravityHeadContactCheckの4つです。

この4つ、名前から察するに…

  • GroundCheck

    • Groundというからには地面のチェックに違いありません

  • JumpControl

    • 読んで字のごとく、ジャンプを制御しそうです

  • Gravity

    • これもそのまま重力を与えてくれそうですね

  • HeadContactCheck

    • 地面のチェックがあるなら頭上のチェックもあるべきです

どうやら、どれもジャンプする際に必要になる要素のように見えますね。
少し順番を入れ替えてみましょうか。

  1. ジャンプ前にGroundCheckによってジャンプ出来る床に立っているか、HeadContactCheckによって頭上に障害物がないかをチェックする。

  2. 障害物が無ければJumpControlによってキャラクターがジャンプする。

  3. キャラクターが滞空していたらGravityによって落下させられる。

  4. キャラクターが落下し、滞空状態か着地したかの確認をGroundCheckが行う。

OK、これから探りを入れる「ジャンプ」というアクションに必要な要素の洗い出しは完璧です。
コンポーネントを上から順に見ていきましょう。

まずはGroundCheckです。


GroundCheck

GroundCheckは今後ほぼ全てのシーンで登場するほど、ポピュラーなコンポーネントです。
一部の特殊な状況を除いて、キャラクターは基本的に地面に立っているので、地面判定を取得するためのGroundCheckはもはや家族、地盤と言ってもよいでしょう。

それでは、彼が持っているパラメータを見ていきます。

ふーん…

まぁ、よくわかりませんがDistance、というからには何かしらの距離を測ってるようですね。
Max Slopeは地面と判定できる角度、といった所でしょうか。

失礼、ちゃんと説明しましょう。


Ambiguous/Precise Distance

Ambiguous Distanceは、Playerが「地面に着地したかもな~?」とする曖昧な距離の設定。
Precise Distanceは逆に「キッチリ着地した」とする正確な着地距離を設定します。

なぜ着地の判定が2つもあるのでしょう?
正確な距離だけあれば良いのでは?

実は、これはCharacterControllerが持つCapsuleColliderに対する補完的な意味合いがあります。
Characterは厳密に両足の裏で地面に立っているのではなく、CapsuleColliderと地面の衝突判定によって地面に立っているように見えています。

CapsuleColliderの形状を思い出してください。

このカーブのところ。ここが厄介なのです。

つまり、このカーブが絶妙な具合に段差に引っかかっていた時、これを床に立っているとするか、滞空状態とするか、何らかの基準で判断しなければなりません。
その為のパラメータが、Ambiguous/Precise Distanceというわけです。

もう少し詳しく解説しましょう。

InspectorでGroundCheckを開くと、SceneView上にこのような2つの球体が表示されますね。

上の球がCharacterControllerと同じ地面判定、つまり「厳密な判定をおこなう球」PreciseDistanceで、下の球がAmbiguousDistanceで設定される「曖昧な判定を行う球」となります。

曖昧な判定…?
一体なんです、それは?


ファジー着地

さて、この「曖昧な判定を行う」機能によって生まれる概念があります。
有名な解説動画にも出てきますが、我々はこれを「ファジー着地」と呼んでいます。

3Dモデルが厳密な着地をしている事よりも、プレイヤーが着地したと思った時に着地判定が取れる事はとても重要です。

ゲームをプレイしていて「えぇ~!?今の着地したやろがい~!!」と、思ったことはありませんか?
平たく言うと、あの感じです。

また、この機能を使うことで、例えば階段を走って降りた時にキャラクターが「ガタガタガタ…」と接地と滞空を繰り返さなくて済むようになったり、ガケのキワキワでのジャンプ、または乗り上げの時にも効果を発揮してくれます。

勿論、ステージを作る上で厳密な処理だけあればよい、例えば死にゲーと呼ばれるジャンルなんかでは、このファジーな要素は全て切り捨て、厳密な判定のみで作った方がより死に易いゲームになるはずです。

少々複雑な内容になってしまいましたが、今は「ふーん」で済ませて大丈夫です。

あるいはAmbiguousDistanceの数値を弄って、階段付近をウロチョロすると、変化を操作で感じられるかもしれません。
もし、ご興味があれば。


Max Slope

Max Slopeは「」と判定する角度の制限です。
60という数字は、傾斜60度までは床として判定しますが、60を超えたら床ではないと判定されます。

この「床判定」はGroundCheckが持つ「IsOnGround」というノードで取得する事が出来ます。

もし床であると判定された場合はtrue、床ではないと判定された場合はfalseが返ります。

シンプルですが、非常に使い勝手の良い機能です。


On Change Ground Object

GroundCheckコンポーネントの下部にCallBacksという機能があります。
本音を言うと、この段階で触れるべきか悩みましたが、これが何なのかだけ、先にお伝えしておきます。

これは俗にコールバックと呼ばれる機能で、TCCのほとんどのコンポーネントが所持しています。

GroundCheckの場合はOn Change Ground Objectの通り、地面のオブジェクトが変化した際に、様々なイベントを呼び出す(Callback)事ができます。

具体的な使い方の解説はしばらく後になりますが、簡単に言うと地面の素材によって出る足音の種類を変えたり、毒沼や氷の上などを察知して移動速度を変化させたりする事ができます。

あ、それできるんだ!?」と思ったでしょう?
出来ます。なぜならゲームを作る時、その機能が必要になるからです。
そして、その機能は地面をチェックする機能の中にあって欲しい。

TCCのコンポーネントにはこういったコールバックが沢山あります。
1機能1コンポーネントがTCCの基本思想であり、コールバックに注目すると、関連する様々なイベントをトリガーできる(場合がある)事を覚えておいてください。

さて、GroundCheckについてはこのくらいにして、次はJumpControl…の前に、対になるHeadContactCheckを見ていきましょうか。


HeadContactCheck

GroundCheckがPlayerの足元をチェックするなら、HeadContactCheckは頭上をチェックします。

こちらはより分かりやすいですね。
Head Position OffsetはPlayerの頭の高さに対して補正を掛けます。

注意点として、このHead Position Offsetの位置はHeadボーンを参照しておらず、CharcterSettingsの中にあるHeight値から相対的に指定されています。

Max Heghtも一見するとHead Position Offsetの位置に見えますが、実は足元からの距離…この場合床から2.5m上までをチェックします。

天井が2mだった場合、常に天井があると判定されますが、3mだった場合は天井はないと判定されます。
勿論1mほどジャンプしたら、2.5mの距離に天井がヒットするので、天井アリとされますし、天井が落ちてきて2.5mの範囲内に入ったら、天井アリとされます。

つまり、脱出ゲームなどで、上からゆっくり降りてくる天井から逃げないといけない時、この機能がとても役に立つというわけですね。


HeadContactCheckの必要性

ジャンプの実装において、このHeadContactCheckを使う場合「天井にぶつかったかどうかを判定する」為に使われるのかと思うかもしれません。

実は天井への衝突は物理的なColliderで判定しており、このHeadContactCheckは無くても動作します。
ではなぜHeadContactCheckを使うのか?

結論から言うと、天井のないゲームでこのコンポーネントは全く役に立ちません。
ただし、JumpControlは天井を考慮した挙動をする為、JumpControlを使う場合はこのHeadContactCheckもセットで使う必要があるのです。

もう少し実装が進むと、HeadContactCheck自体も使いこなす必要が出てきますので、今は一旦機能概要だけ覚えておきましょう。


Gravity

お次のGravityは文字通り、Playerに重力を与えます。

パラメータもGravity Scaleだけなので分かりやすいですね。
この数字が大きいほど、重力は強くなります。

当たり前ですが、このGravityという概念がなければPlayerは落下しません。
こちらもJumpControlを使う以上、必須のコンポーネントとなっており、ジャンプを手軽に実装するために、セットで運用するものとして覚えておきましょう。

勿論0にすることで無重力にすることは可能ですよ。


Jumpの実装方法

実のところ、ジャンプの実装には様々な方法があります。

例えばRigidBodyのForce機能を使えばUnity標準の物理機能だけでキャラクターを飛ばして落下させることも出来ます。
しかし、もし貴方がゲーマーなら、きっとこう思われるでしょう。

ジャンプはゲームの数だけ存在する」と。

JumpControlには、その様々なジャンプを可能にするための機能が搭載されています。
さぁ、次はいよいよ最後のJumpControlコンポーネントを見ていきます。


JumpControl

いよいよ本命のJumpControlです。
流石にパラメータ数も多めになっていますね。

数は多めですが、内容自体は割とシンプルに見えます。
具体的な効果は実装と照らし合わせながら見ていくとして…おや?

一つ、これまでと大きく違うところがありますね。
そう、Callbacksが使われています


Callbacks

失礼、先ほど具体的な解説は当分先と言いましたが、あれは嘘でした。

実のところ、プログラムのプの時も打ったことがない私は、コールバックという機能を使ったのもTCCが初めてで、見慣れない文字や慣れないUIを前に「こんな難しい機能使いこなせる気がしない…
そう思っていました。

しかし実際に使い始めてからは、どのコンポーネントでも同じ形でしか使わない事がわかったので、一つ覚えてしまえば、後は考えること無く、同じ設定にするだけでした。

JumpControlはまさに、これを習得するのにうってつけのコンポーネントというわけです。

さぁ、Graphを見ていきましょう。


Graph

Newが2つとModifiedが1つ、それから…今回もまた注釈がありますね。
まずは注釈から見ていきましょう。

なるほど、確かにSprintは走る為の機能なので、移動を制御するMoveの中に格納するのは合理的です。
念の為、中身を確認してみましょうか。

下段に走る為の処理が追加されていますね。
中身は特に何も変わっていないようです。確認が済んだら戻りましょう。


Ground Check

それではNewのうちの…そうですね、Ground Checkの方から中身を見ていきますか。

ダブルクリックで開くとこのようなGraphになっていると思います。

例によって注釈から見ていきましょう。

分かりやすくていいですね。
つまり、Animatorに何らかのParametersが追加されてる可能性があります。
先にそちらも見ておきましょう。

予想通り、IsGroundというParametersが追加されていました。
加えて、ジャンプ用のアニメーションが一通り登録されてるようですね。

察するに、ジャンプボタンが押されたらJumpStartが再生され、滞空状態にループ再生されるInAirと、着地した時に再生されるJumpLand、といった所でしょう。

となれば、IsGroundが使われる場所は一つだけです。

ここです!
InAirというStateからJumpLandに遷移するにはIsGroundがtrueである必要があるようです。
つまり、IsGroundがtrueにならない限り、ずっと滞空モーションを取り続けるわけですね。

ん?
JumpLandをよく見るとBrendTree Parametersという表記が見えます。
ということはこのStateも複数のモーションが設定されてるという事でしょうか。

ダブルクリックして中身を見てみると…

まさかの3つでした!
さて、これは一体…フムフム…なるほど。

どうやらこのBlendTreeでは着地した時に、何らかの移動入力があり、キャラクターがSpeedを持っていた場合、移動しながら着地する為のもののようです。

確かに、着地するたびにその場で止まっていてはテンポが悪くなってしまいますし、このワンクッションがあると操作に滑らかさが出るような気がします。

少し話が横道に行ってしまいましたが、Animatorのチェックが出来たのでヨシとしましょう。
再びGraphに戻ってください。

ご丁寧にノードの解説がついていました。

ここではGroundCheckのIsOnGroundが使われているようですね。
このノードは注釈にある通り、Playerの接地状況をTrueとFalseで返してくれるノードです。

このSubGraphはOnUpdateによって常にPlayerの接地状況をAnimatorのIsGroundに転送しているだけのようですね。

地面判定がたったこれだけで済むなら楽なものです。


Jump

それではもう一つのNew、Jumpを見ていきましょう。

開いてみると…むむ、これはまた…良く言えばカラフル…正直、注釈が少々鬱陶しいですね。

まぁきっと親切心によるものでしょうから、無下にせず一つずつ読んでみますか。

でしょうね。
流石にこれは知ってました。

ノードグループの上半分から見てみましょう。

OnInputSystemEventは前にも出ましたね。
ActionMapに登録されたJumpがOnPressedされた時、JumpControlのJumpが発動する…のでしょうか?

ひとまずActionMapを見てみましょう。

ちゃんとJumpが登録されていますね。
キーボード・マウスならSpace、ゲームパッドならSouthボタンがアサインされています。

それでは次に下部を見ていきますが…なにやら見慣れないノードがありますね。
紫の枠で囲われていて、重要そうなオーラを醸しています。

このノードはUnityEventと呼ばれるノードで、ここに書かれた文字列のトリガーが呼ばれた時に動作を開始します。

OnJumpStart…はて、どこかで見かけたような…?

思い出しました。
JumpControlコンポーネントにあるCallbacksにそう書かれていましたね。

TCCが持つCallbacksを使う場合は、UnityEventとの連携が必要になります。

ね?私が面倒くさ…もとい「難しそう」と思った理由がわかるでしょう?

ですが、この機能は本当に便利なので、是非覚えて頂きたいのです。
きっと未来の貴方は、今この瞬間の事を何度も思い出すでしょう。


Callbacks

さて、Callbacksに書かれた「On Jump()」の正体について説明します。
これはJumpControlのJumpによって呼び出されます。

つまりここですね。
このJumpによって、On Jump()に登録された「OnJumpStart」というUnityEventが発行されるのですが、重要なのはOnJumpStartの上にあるScriptMachine.TriggerUnityEventという項目です。

このセレクトボックスを開くと…

あぁ、なんてことだ。
一体なんですこれは?これを全部理解しなければならないのですか?

答えはNoです!
現時点でこれらを覚える必要は一切ありません

我々はこの中からScriptMachineというカテゴリの中のTriggerUnityEventを選ぶ。それだけです。
もし貴方がScriptMachineではなくStateMachineを使っていた場合は、StateMachineというカテゴリに置き換わっているはずです。

大丈夫、気を強く持ってください。
私は今もこれ以外知りませんが、全てのサンプルシーンを作成できました。


CallbacksのUI

しかし、正体不明な物を正体不明なまま放置しておくのは、文明人として聊か癪に障る物があります。

そこで、このような図を用意してみました。
Callbacksはこの四つのブロックから出来ています。

こうして分割して見て見ると、それほど複雑な物ではないのですが、普段から開発に馴染みが無い人は、新しいUIを見ると「ウッ…」となるのも理解できます。何故なら私がなったからです。

念の為補足しますが、左下の「対象とするオブジェクト」はヒエラルキーからD&Dで持ってくる事が出来ます

Callbacksの説明についてはこのくらいにしましょう。
これ自体は今後もしこたま登場しますが、そのどれも同じ設定である事だけ覚えておいてください。


UnityEvent

さて、Callbacks君によって発行されたOnJumpStartはUnityEventを介して実行されます。

このOnJumpStartにより、AnimatorのJumpStartというStateがPlayされ、キャラクターがジャンプアニメーションを再生する、というのが、このシーンのジャンプの仕組みです。

なんて回りくどい!

実はこの回りくどさにはちゃんと理由があります。
それが注釈にかかれたコレというわけですね。

何故コールバックを経由するのか
InputからのフローにPlayを入れた場合、ジャンプ処理が行われたかどうかに関わらず、入力があった時点でジャンプのモーションを再生してしまいます。
コールバック「OnJump()」はジャンプ処理が行われた時に発行されるので、必ずジャンプ処理のタイミングでジャンプモーションが再生されるようになるのです。

なるほど、そういうことなら理解できなくもありません。
折角なので、もう一つの注釈も読んでおきましょうか。

GroupColor
VisualScriptingには検索機能がないので、カスタム可能なイベントトリガー類はそれ単体で探しやすくしておくのがオススメです。
例えば特徴的な色のグループに入れておく、位置をずらす、グループ名をトリガー名にするなどが効果的です。

こちらはこの紫のフレームについての注釈ですね。
なるほど、重要そうなオーラを放っているだけはあります。
見つけやすくするための工夫だったのですね。

グループ名にトリガーの名前を入れておくと、GraphViewを引いたときでも発見しやすくなるメリットがあります。
問題が発生した時に少しでも探す手間を省く小技といったところでしょうか。


まとめ

Move、Sprintが簡単だったので油断していました
まさかこんなに複雑な事をさせられるとは。

しかし、これで我々はCallbacksを使うという手を入手出来た事になります。
今後も度々登場する機能ですが、この機能の便利さを解説するのはもう少し先になるかもしれません。

大丈夫、今度は本当です。