ThirdPersonStoryAdventureTemplateの使い方

はじめに

世の中にはアクション、RPG,シミュレーション、パズルなどなど様々なジャンルのゲームがありますが、アドベンチャーゲームは自分で作った世界を表現するストーリーテリングにはピッタリのゲームジャンルです。

しかもアクションゲームほどシビアな処理を書く必要がなく、比較的アセットの導入もしやすいため、ゲーム制作初心者が作る最初の一作目として個人的にはとてもお勧めです。

…とはいえ、とはいえですよ。

比較的難易度低めのアドベンチャーゲームといえども、キャラクター移動、インタラクション、アイテムシステム、会話、カットシーン、レベル遷移、セーブロードなど、ぱっと思い浮かぶだけでかなりの実装が必要そうです。

これ全部一人で実装しようとすると、それはもうかなりの労力が必要なわけでして、生半可な覚悟では心がポッキリ折れちゃうの間違いなしです。

そこで、今回お勧めするThirdPersonStoryAdventureTemplateです。

このアセットには、嬉しいことに上記実装が全て含まれています。

あとは自分で作った、もしくはアセットストアで買ってきたキャラクターや背景を当て込んで、NPCやアイテムを配置して謎解き・会話シーンを作り、カットシーンでストーリー進行を盛り上げていけば、ゲームとして成立しちゃうという寸法です。

しかも、このアセットはマーケットプレイスで2021年6月の無料コンテンツに選ばれているので買い逃していなければ、無料で始めることができます。

もう後は作るしかないでしょ!

ってとこなんですが・・・。

ドキュメントは英語で、現状ちょっと取っつきが悪い部分があるのは否めません。

そこで、本記事では公式のTPSAT_Documentaion.pdfを日本語化し、多少かみ砕いて説明を加えたものをお届けしてみようかと思います。

本家pdfを呼んでいる方にとっては被っているものとなりますのでご了承ください。

記事公開の許可をくれたDennis Paulyさん、ありがとうございました!

※使用しているバージョンはUE4 26.2、アセットは2021/06/19時点で公開されている最新版となります。

※ThirdPersonStoryAdventureTemplateと表記すると長いので、本記事中ではTPSATと略します

ThirdPersonStoryAdventureTemplateについて

このテンプレートで出来ること

このテンプレートはプレイヤーキャラクターが画面中央に表示された状態でゲームが進行する、いわゆるサードパーソンビュータイプのゲームを作るためのものです。

マップ上に配置された家具、道具、アイテムなどのオブジェクトを調べたり、取得したり、使ったり、またNPCと対話を行ったりするシステムが実装されています。(これを総じてインタラクトと呼びます)

公式のドキュメントには「Life is Strage」や「The Walking Dead」などからインスピレーションを受けたと書いてありますね。

このテンプレートはどのような人向けか?

プログラムの専門的知識はないけど自分のアイディアをゲームにしてみたいプランナー、デザイナーの方々にはピッタリのアセットです。もちろん、プログラマさんでも開発時間を短縮させるためには非常に有益かと思います。

テンプレートで実装されている機能

このテンプレートでは以下のような機能が実装されています。

  • サードパーソンゲーム用の機能が実装されたPlayerController、Pawn、AnimationBlueprint
    これらはUnrealEngineでゲームを動かすために必要となる基本クラスで、TPSATではさらにサードパーソンゲーム向けの機能をセットアップした状態で提供されています。
    それぞれのクラスの役割は、プレイヤーの操作を受け取るPlayerController、ゲーム内でキャラクターとして配置されるPawn、PawnをアニメーションさせるためのAnimationBlueprintとなっています。
  • シーケンサーによるシネマティック演出を使ったインタラクション
    UnrealEngineには操作入力を中断し用意しておいたカメラワークやキャラクターのアニメーションを再生し映画のカットシーンのような演出を行うシーケンサーという機能がありますが、TPSATにはゲーム中でNPCやアイテムにインタラクトしたときに、シーケンサーを実行する機能が実装されています。
    これにより、いわゆるイベントシーンを実現することが可能です。
  • 3Dスぺース上に表示されるインタラクションウィジット
    TPSATではプレイヤーがレベル上に配置されているアクセス可能なオブジェクトやNPCに近づくと、どういったインタラクトを行うか選択させるUIを3D空間上に表示します。
    UIは作るの結構面倒なので、これのおかげでサクッと実装が済んでかなり助かります。
  • ジャーナルやアイテムの収集・表示システム
    TPSATにはアイテムやジャーナルに関するシステム全般が実装されています。
    そのためゲーム制作者はアイテムやジャーナルに関する情報や動作を設定し、それをレベル上に配置するだけで、プレイヤーはそれらを集め、説明を表示し、使うことが出来るようになります。UIもセットとなっているので非常に便利です。
  • マウス・キーボードとゲームパッドのサポートと自動切換えシステム
    マウス・キーボードとゲームパッド両対応の実装は非常に大変なのですが、TPSATには標準でこの機能が組み込まれています。
    入力デバイスに応じてメニューの操作表示も切り替えてくれます。
  • メインメニュー、インゲームメニューとグラフィック・オーディオ・ゲームオプション設定
    ここでいうメインメニューはタイトル画面に相当するもので、そこからグラフィックやオーディオなどの各種オプション設定メニューへ移行することも可能です。インゲームメニューは、ゲームプレイ中に呼び出すメニューで、アイテムやジャーナルに関するUI表示、ゲームセーブ呼び出し、ゲーム終了といった利用できます。
  • セーブロードサポート
    TPSATは3つのセーブゲームスロットを持っており、それぞれにゲームの進行状態をセーブすることが出来ます。TPSATのルールに従ってアイテムやジャーナルを作成、配置すれば、セーブゲームで自動的にそれらの状態を保存してくれます。
    もちろん独自に進行状態を管理するデータを作ってセーブ、ロードに対応させることも可能です。
  • レベル遷移・レベルストリーミング機能
    レベル遷移というのは、いわゆるマップチェンジのことです。
    タイトル画面からゲームマップへのチェンジ、ゲームプレイ中でのマップ切り替え(出入り口から次のフィールドへ移動など)をサポートしています。
    ちょっと高度な使い方ですがマップが非常に大きい場合は小分けにしてゲームを止めることなく必要な領域だけマップを読み込むレベルストリーミング機能を使うこともできます。
  • 多言語サポート
    オプション画面で言語切り替えを行うことにより、ゲーム内で使われる言語を切り替える機能をサポートしています。切り替えるシステムは存在しますが、各言語ごとのデータはもちろんゲームの作り手が用意する必要があります。
  • サーフェースタイプによる足音の切り替え
    キャラクターが歩くときに鳴る足音を、地面の種類(サーフェースタイプ)に応じて切り替えるシステムが用意されています。土、コンクリート、木などを地面に適切に設定してあげれば自動的に足音が切り替わり、ゲームの臨場感をアップさせることが出来ます。

インタラクティブアクター(プレイヤーがアクセスできる配置物・人)

TPSATにはプレイヤーがアクセスできる配置物として様々なタイプのアクターが用意されています。これらをインタラクティブアクターと呼びます。

プレイヤーがインタラクティブアクターに近づくと、その名前と、どういったインタラクトを行うことが出来るか(一例をあげると、調べる、取る、話す、動かすなど)を表すインタラクションアイコンを表示します。

インタラクトにはそれぞれメッセージ、セリフ、ボイス、効果音を割り当てておくことができ、プレイヤーが該当インタラクトを実行したときに、それらを再生させることが出来ます。

他にも、インタラクションアクターには以下のような機能があります。

  • アクターへのフォーカス
    アクセス時にカメラの視点をアクターへ向ける。
  • 調べる(2D)
    ゲーム中、プレイヤーがアクターを調べた時に、アクターに関する情報を2Dウィジェットで画像として表示させることができます。画像はいくつか用意しておくことによって、ユーザーの操作でページ送りするような操作も可能です。
  • 調べる(3D)
    ゲーム中、プレイヤーがアクターを調べた時に、対象アクターをプレイヤーカメラ中心にもってきてユーザー操作で回転して調べさせることができます。
  • 収集
    アクターをアイテムとして収集することができます。収集時に既定のアニメーションシーケンスを再生させることが可能で、上記の「調べる」操作を行った後にさらに決定ボタンを押した時に収集させる、といった設定も可能です。
  • ジャーナルへの追加
    対象のアクターを調べたときにジャーナルに情報を追加することが出来ます。
  • シーケンスのループ
    それぞれのアニメーションシーケンスを設定しておくことによりインタラクト実行時に、最初にイントロシーケンス再生、インタラクト中ループシーケンス再生、インタラクト終了時にエンドシーケンス再生、その後、ゲームプレイに戻る、といった遷移をさせることができます。
  • NPCとの会話プレイヤーによるセリフの選択により分岐するNPC会話シーンを作ることが出来ます。

構成とセットアップ

最初に知っておくべきこと

TPSATは簡単で使いやすいように設計されていますが、メインとなるコンポーネントはどのような構成でどんな種類があるかの知っておく必要があります。

Blueprints->Coreフォルダの中にはTPSATのコアとなるブループリントがあるので、一つずつ見ていきましょう。

ゲームインスタンス

TPSATではゲームインスタンスとしてBP_StoryAdv_GameInstanceを使用します。

ゲームインスタンスで設定された値はレベル遷移しても保持されるので、ユーザー設定やレベルストリーミングの制御、セーブ・ロードの実装に使われています。

他のアクターからも必要に応じてそれらの機能にアクセスすることも可能です。

ゲームコントローラ

TPSATではゲームコントローラーとしてBP_StoryAdv_GameControllerというアクターを使用します。このクラスにはTPSATでインタラクションを実現するシステムが実装されており、ゲーム内のメニュー、プレイヤーデータのバックアップ、デバイスからの入力など様々な処理を行う最も重要なアクターの一つです。

メインメニューのレベルでない限り、パーシスタントレベルには常にこのインスタンスが存在する必要がありますが、通常このアクターはゲームモードにより自動生成されるため手動でレベルに配置する必要はありません。

ゲームモード

ゲームモードはゲームのルールや仕組みを管理するために使われるクラスで、使用するポーンやコントローラーなどのクラス制御も行っています。

TPSATでは、このゲームモードにBP_StoryAdv_GameModeを使用しておりゲーム設定の制御や入力デバイスの変更を行う関数を呼び出しています。

また、TPSATはゲームのメインメニュー用に別のゲームモードを使っていますが、これも基本的にはBP_StoryAdv_GameModeを派生させたものです。

メインメニューのゲームモードではゲームキャラクターではなく、メニュー用に用意された専用のポーンを生成します。

メニューポーン

メニューポーンはメインメニューのレベルでのみ使用されます。

メニューを動作させるために、プレイヤーの入力を受け取ってそれをメインメニューのウィジェットに転送します。

インターフェース

TPSATでは、クラス間で多くのやりとりが行われています。

BP_InteractionInterfaceは、インタラクティブなアクターから情報を取得したり、実際のインタラクションに使用されます。

BP_WidgetInterfaceは、インタラクティブなアクターから情報を取得したり、インタラクションを実行する際に使用します。

インターフェイスに慣れていない方は、TPSATの仕組みを深く理解する前に、まずUE4インターフェイスがどのように機能するかを確認してください。

関数ライブラリ

関数ライブラリBP_StoryAdv_FunctionLibraryにはTPSAT内の様々なアクターで使われるヘルパー関数が多数用意されています。特にTPSATで管理している特定のアクタを取得するためによく使われ、またウィジェットの可視性をトグルするのにも使用されます。

今後のアップデートでの競合を防ぐためにも、ゲーム制作者はこのライブラリに直接関数などは追加せず、必要に応じて個別のライブラリを作成することが推奨されています。

新しいゲームの開始

新規ゲーム開始時にロードされるデフォルトマップは、BP_StoryAdv_GameInstance内の「NewGameLevel」という変数の値を変更することで設定します。この変数はカテゴリに入っていないので、ブループリントを開くとすぐに見つけることができます。 最初にゲームレベルデータテーブル(DataTables->GameSettings->GameLevels_DataTable)に使用するゲームレベルを追加し必要な情報をすべて埋めてください。これに関する詳細は、「レベルの移行とストリーミング」でさらに詳しく説明します。

プレイヤーキャラク

TPSATのプレイヤーキャラクターには、一般的なカメラ設定の他に、カメラの焦点深度の調整、プレイヤーの動き、頭の回転、IKチェーンを使ってアイテムを拾う際の手の目標位置設定などの機能が含まれています。

これらは、TPSAT標準のサードパーソンビューのアドベンチャーゲームに合わせて設定されていますが、もちろん、自分のゲームデザインに合わせて変更することも可能です。

自分のキャラクターをどうやって登場させるか?

TPSATではUE4標準のマネキンがプレイヤーとして使われていますが、当然これを差し替えることができます。

TPSATで提供された機能を維持したままプレイヤーキャラクターを変更するためには、BP_StoryAdv_Characterを派生させた新しいクラスを作成するか、用意したキャラクタクラスの基底をBP_StoryAdv_Characterに変更することが推奨されています。

これらの方法を、将来的にTPSATがバージョンアップしても大きな変更をせずに対応させることが可能です。

さらに上記対応を行った上で、新しいキャラクターメッシュのアニメーションブループリントを設定する必要があります。

TPSATではアニメーションブループリントとしてAnimBP_StoryAdv_Characterが使われているのでスケルトンが同じであれば、これをリターゲットするだけで問題ありません。スケルトンが異なる場合はリターゲット自体は可能ですが、そのうえで元のアニメーションを削除して独自のものに置き換える必要があります。

AnimBP_StoryAdv_Characterではあまり多くのことが行われていないので、自分のアニメーションブループリントに反映させるのは非常に簡単です。

インタラクションシステム

TPSATのインタラクションシステムは、4-アクション-システムがベースになっています。

4-アクション-システムというのは、1つの対象に対して最大4つのインタラクションが可能で、上ボタン、左ボタン、右ボタン、下ボタンと4つのインタラクションを割り振ることが出来る仕組みです。

TPSATはゲームパッドでのプレイを想定しているため、これらのボタンはゲームパッド右側のフェイスボタンに対応しています(A、B、X、Y)。

ボタンごとに異なるメッセージを対象のアイテム・オブジェクト・NPCに送信し、その対象はそれに個別に反応することができます。

インタラクティブオブジェクトとアイテムデータ

インタラクティブなオブジェクト

インタラクトできるオブジェクトはスタティックメッシュを使用しているか、スケルトンメッシュを使用しているかによって、BP_Base_InteractiveObjectまたはBP_Base_InteractiveCharacterのいずれかの継承クラスとなります。

これらのアクターはインタラクション機能を制御し、設定したデータテーブルからデータを読み取り、UIウィジェットの表示/非表示を実行します。

インタラクションシステムはメッシュコンポーネントのオブジェクトタイプ (Collision Presets内のObjectType)がInteractiveに設定されているものを検知します。

プレイヤーが、これらのアクター(やその子アクター)に近づくとアクターに設定されたインタラクトに関するデータが読み取られ、それに従ってUIウィジェットが表示されます。

インタラクトが開始されると、対象のアクターのインターフェースにメッセージが送られ対応するインタラクションイベントが実行されます。

TPSATで用意されているインタラクティブアクター

TPSATには独自のインタラクティブアクターを作成するために利用できる親クラスがいくつかあり、Blueprints->Core->BaseActorsの中にまとまっています。

これらのアクターは、すべてデータテーブルからデータを取得して動作するので、適切なデータテーブルに行を用意するだけで、ゲームの世界に新たなアイテムを設定することができます。それらのクラスを以下に一つずつ紹介していきます。

BP_Base_InteractiveObject

このクラスはスタティックメッシュを使ったインタラクティブなオブジェクトすべての基底となっているクラスです。これを継承させると基本的なインタラクティブ機能を持った独自のスタティックメッシュアクターを作ることが出来ます。

BP_Base_InteractiveCharacter

BP_Base_InteractiveObjectのスケルタルメッシュバージョンです。

これを継承させると基本的なインタラクティブ機能を持った独自のスケルタルメッシュアクターを作ることが出来ます。

BP_Base_SimpleInspection

プレイヤーがアクセスするとカメラが、このオブジェクトを向き、必要に応じて設定されたダイアログを表示させることが出来ます。

BP_Base_InspectionCloseUp_2D

このクラスをインタラクトすると、あらかじめ定義されたフルスクリーンのユーザーインターフェースウィジェットが表示されます。

このウィジェットは複数のページを持つことが可能で、プレイヤーの操作でそれをページ送りさせることが出来ます。

BP_Base_InspectionCloseUp_3D

このアクターをインタラクトすると、オブジェクトがプレイヤーのカメラの近くに移動し、ユーザー操作によりメッシュを3D空間で回転させることができます。アイテムは元に戻したり、拾ってインベントリに入れたりすることができます。インタラクションの際には、あらかじめ定義されたダイアログを表示することもできます。

BP_Base_LoopingSequence

このクラスでは、3つのレベルシーケンスを設定することができます。1つはイントロ、1つはループ、1つはエンディングのシーケンスです。

インタラクション開始時にイントロのシーケンスが再生され、それが終わると、ループアニメーションの再生がシームレスに再生、プレイヤーは入力によってそれを終了することができ、エンディング・シーケンスが再生されます。

それが終わるとゲームプレイが再開されます。

シーケンスが再生されている間に、音楽トラックやプレイヤーのダイアログを流すこともできます。

雰囲気の演出を行うために様々な使い方ができる機能で、例えば、BGM付きの休憩スポットとして、プレイヤーやキャラクターに考える時間を与えることができます。

BP_Base_SimpleCollectable

このクラスは、プレイヤーがインタラクトするとアイテムとしてインベントリに追加されるクラスです。

アイテム追加時に、いろいろな動作を行わせることが出来ます。

あらかじめ定義されたレベルシーケンスを再生してアイテムを拾うこともできますし 、IK ボーン調整を使用して単純なテイクアニメーションを再生し、ワールドスペースでキャラクタの手をアイテムに近づけることもできます。

また、プレイヤーがアイテムを持っている間、アイテムはインベントリにのみ表示されるようにしたり、あらかじめ定義されたアイコンを画面に表示させることもできます(キーアイテムを拾ってすぐに使う必要がある場合などに便利です)。

その他のフォルダにあるクラスについて

その他のクラスは特定のケースで使用されます。対話用NPC、レベル移行用アクター、使用アクションについては、それぞれのトピックで言及します。 

テーブルに格納されているデータ

ゲームのアイテム情報を整理するため、TPSATではすべてのアイテム情報はデータテーブルに保存されています。 ゲームワールドに配置したインタラクティブアクターには、テーブルと行名を指定する部分があり、これによりそのアイテムのオブジェクト名、インタラクションラベル、ダイアログデータ、その他プロパティが決定されます。

こういった設計は、ゲーム制作者がゲーム内に登場するすべてのアイテムを包括的に把握し、ゲームが破綻しないよう管理するために非常に有用な仕組みです。

アイテムの使用

アイテムはインベントリを開いて使用することができます。

アイテムを使用するには、そのアイテムのInventoryDataの項目に使用アクションを割り当てる必要があります。

使用アクションはBP_Base_UseActionを派生させたブループリントクラスである必要があり、アイテム使用時にオブジェクトが生成されイベントが実行されます。アクション終了時には自分自身を破棄する必要があります。

アイテム使用アクションを自作する場合、アイテム使用時には関数 "RunScript"が実行されるので、これをオーバーライドし、親クラス呼び出しを行ってください。

その後ろに必要な機能を記述し、処理が完了したら自身のアクタを削除するようにノードを記述してください。

なお、アイテムの「Discard when used」フラグが有効になっている場合は、使用時にインベントリからアイテムが自動的に削除されます。

会話

プレイヤー単体のセリフ

オブジェクトを調べた時などに再生されるプレイヤー単体のセリフもデータテーブルに用意します。

デフォルトでは「DataTables->Dialogues->ObjectDescriptions」に記述されています。

このテーブルには、各項目ごとに、再生するセリフの音声データや表示する字幕などの情報が並んでいます。データは配列要素として提供されているので、複数行のテキストを再生することも可能です。

TPSATのサンプルでは、最初の部屋(ShowRoom)に配置してあるアイテムは「DataTables->ItemData->ShowroomItems」に記述されている設定で動作するようになっていますが、この中で各アイテムをプレイヤーが調べた時に「DataTables->Dialogues->ObjectDescriptions」の記述に従ってセリフ表示・音声再生を行うよう設定されています。

字幕の表示

TPSATの字幕は、エンジンに標準で用意されている字幕システムに基づくものではありません。

TPSATで用意されているウィジェットを使い、プレイヤーのセリフ」で説明したデータテーブル(ObjectDescriptions)に記述されたテキストを表示しています。

字幕の1行あたりの表示時間は文字数に依存するため、短い文章は長い文章よりも早く自動的にフェードアウトします。 視覚的な実装は、WBP_Subtitlesで変更できます。

NPCとの会話

会話するNPCを作るためにはBP_Base_DialogueNPCを派生させたクラスを作成し、そこに必要な設定を行っていきます。会話に関する設定はConversation Setupという項目にまとめられています。

プレイヤーとNPCとの会話には、2つのデータテーブルが必要です。

1つのデータテーブルには、プレイヤーのセリフ設定がすべて入っており、もう1つのテーブルにはNPCのセリフ設定がすべて入っています。 TPSATのサンプルではTPSAT->DataTables->Dialogues->ExampleConversation内のExampleConversation_PlayerとExampleConversation_NPCがそれに該当します。

NPC用のデータテーブルにはセリフごとに再生するレベルシーケンス、表示する字幕テキスト、字幕テキスト表示時間、次に表示するセリフのデータテーブルと行の指定が存在します。

一方、プレイヤー用のデータテーブルにも再生するレベルシーケンスの指定はありますがこちらはループ再生用となっています。これは、プレイヤー側のセリフはユーザーが選択できるようになっているためです。

プレイヤー用のセリフは、選択肢ごとに複数記述できるようになっており、次に表示するNPCのセリフも分けて設定できるので会話の分岐が可能となっています。

プレイヤーの選択結果の反映

NPCとの会話においてプレイヤーの選択によって何らかの処理を行いたい場合は、BP_Base_ConsequenceScriptを派生させた新しいクラスを作成し、その新しいクラスの中にある「Run Script」という関数をオーバーライドします。

この関数に、ドアをロックする、別の会話を可能にするなど、行いたい処理を記述していきます。関数が呼び出された後はこのアクタは必要ありませんので、関数の最後に自分自身のアクタを破棄することを忘れないでください。

プレイヤーの選択結果に上記クラスの処理を追加するには、プレイヤーの会話用データテーブルで選択肢を記述するOptionsという項目の中のConsequence Scriptに作成したクラスを設定します。

会話の例とBP_ExampleConsequenceScriptを参考にしてみてください。

レベル移行とストリーミング

レベル移行時にゲームの状態を維持させることについては、あまり気にする必要はありません。 BP_StoryAdv_GameInstanceへの参照を取得し"Level Transition"という関数を呼び出してパラメータに移動したいレベルを指定します。

この時注意しなくてはならないのは、レベル指定はアセットのレベル名を直接指定するのではなく、後述するレベル移行用のデータテーブルに必要なレベル情報を登録しておき、その行名を指定する必要があるということです。

また、パーシスタントレベルの移動ではなく、プレイ中にサブレベルをストリーミングイン、アウトしたい場合は、BP_StoryAdv_GameInstanceの"Stream in and Out"という関数を使用します。

さらにレベル移行をさらに簡単にするために、BP_LevelTransitionActorというマスターアクターが用意されています。

このアクターにレベルやサブレベルの名前を指定しておけば、インタラクトする際に定義されたレベルを自動的にロードしてくれます。また例えば、ドアから出るなど、事前に再生されるべきシーケンスを指定することもできます。

ゲームで使うレベルはすべてDataTables->GameSettingsの中にあるGameLevels_DataTableというデータテーブルに登録しておく必要があります。

ここには各行ごとに、レベル説明、メインレベル名、サブレベル名配列、レベル移行時に表示するサブレベルのリスト、チャプタータイトル(セーブゲームスロットの表示に利用される)が設定されており、"Level Transition"でレベル移行を行うときには、このデータテーブルと行を指定することになります。

なお、レベル説明については開発時の参考用にのみ利用され実際のゲームで使われることはありません。

多言語対応

TPSATはローカリゼーションに対応しています。

ほとんどのユーザーインターフェースのテキストは文字列テーブルに集められており、異なる言語用のデータセットを簡単に作成することができます。

メニューで言語を切り替えると、これらのユーザーインターフェイスの文字列に使われるデータセットも自動的に切り替わります。

UIのカスタマイズ

Blueprints->Core->Settingsフォルダ内には、TPSATの設定を変更できる構造体やテーブルが入っています。この中のBP_UserInterfaceButtons_Gamepad、BP_UserInterfaceButton_MouseKeyboardにはTPSATのUIで表示されるゲームパッドやマウス&キーボードのボタン画像が定義されており、ここを変更することにより任意のものに差し替えることが出来ます。

UI表示を行っている全アセットは、これらの構造体の値やデータテーブルで設定された値を読み込んで使うので、表示をカスタマイズしたいときに各ウィジェットごとに画像、フォント、色を手動で置き換える必要がないようになってます。

ただし、いくつかの設定のカスタマイズでは、そのような作業が必要になることもありますのでご注意ください。

便利な機能

TPSATには便利な関数がいくつか実装されています。

以下は、自分でゲームを作る際に役に立つ関数でしょう。

DisplayMessage

GetGameControllerで、BP_StoryAdv_GameControllerの参照を取得し、DisplayMessage関数を呼び出せば、任意の文字列を画面上にフェードイン、フェードアウト付きで表示させることができます。

関数ライブラリに用意されている機能(すべてのアクターから呼び出し可能)

HideActiveWidgets,ShowActiveWidgets

シーケンス再生中などに、一時的にウィジェットの非表示/表示を切り替えるにはHideActiveWidgets,ShowActiveWidgetsを使用します。

 

IsItemInInventory?

この関数は、その名の通り特定のアイテムがプレイヤーのインベントリにあるかどうかを返します。

GetStoryAdvGameMode,GetStoryAdvGameInstance,GetGameController

これらの関数は、TPSATのゲームモード、ゲームインスタンスゲームコントローラーへの参照を取得できます。

そこから関数を呼び出したり、情報を取得したりするのに役立ちます。

変更履歴・アップデートに関するヘルプ

TPSATにバージョンアップが入った場合に既存プロジェクトにそれらを適用するには、まず最初に最新版のテンプレートを使って新しいプロジェクトを作成し、その中でバージョンアップが入ったTPSATのファイルを個別に既存のプロジェクトに移行することをお勧めします。

移行する際には、移行先のプロジェクトを閉じておいてください。

あるファイルを移行する際、エンジンは他のファイルも移行しようとします。混乱を防ぐために、移行したいファイルには「置き換え」を、それ以外のファイルには「すべてなし」を押してください。

バージョンアップによりTPSATのどのファイルに変更が入ったかのチェンジログはディスコードで調べることができます。(https://discord.gg/ekhzfCW)

最後に

さて、以上でドキュメントに書かれていることは大体網羅した感じではあります。

が...自分で記事書いておいてこんなこと言うのはなんですが、これだけだと絶対資料足りないと思います(笑)

より具体的に、どういうケースではどのクラスをどのように派生させてセットアップすればいいか突っ込んだ解説がないと、さくっと作るのは難しそうです。

Twitterで反響あればもっと使い込んでその辺の解説を行う追加記事を出したいなと思っております。

それでは、また次回の記事で!

 

 

 

 

PhysicalAnimationではこちゃんをうねうねさせるお話

f:id:nca03132:20201213205655p:plain

最近、なんさんのBlender入門を履修してまして

この中で、直方体のハコにボーンが入ったモデル(通称はこちゃん)をくねくねさせるアニメーションを作るレッスンがあるんですが、これを再生しつつ物理も適用できたら面白そうだなぁって思って実験してみたので記事にしました。

ブループリントの作成

ブループリントの新規作成

まずは、はこちゃんのブループリントを作ります。

Actorベースでブループリントを作成しSkeletalMeshComponentをルートコンポーネントとしてくっつけます。今回はBP_Hakochanという名前でブループリントを作りました。

f:id:nca03132:20201213211353p:plain

メッシュとアニメーションの設定

つぎにくっつけたスケルタルメッシュコンポーネントを選択し、スケルタルメッシュとアニメーションを設定します。

f:id:nca03132:20201213211742p:plain

f:id:nca03132:20201213211758p:plain

この例ではメッシュはHakochan、アニメーションはUse Animation AssetでHakochan_Animというアニメーションファイルをループで使ってます。

物理シミュレーション有効化

スケルタルメッシュコンポーネントのSimulate Physicsを有効にします。

f:id:nca03132:20201213212345p:plain

また物理挙動でコリジョン設定が必要なので適切なものを設定します。

今回は物理挙動するActorとして動かすのでPhysicsActorにしておきます。

f:id:nca03132:20201213212610p:plain

これでレベルに配置したときに物理挙動で動くようになります。

動かしてみると・・・

f:id:nca03132:20201213212813p:plain

スクショではわかりませんが、アニメーションしてません。

止まってます。死んでます。完全にラグドールです、本当にありがとうございました。

PhysicalAnimationコンポーネント

スケルタルメッシュにアニメーションを指定して物理を有効にすると、物理の挙動が優先されてしまい、アニメーションが再生されません。

これを解決するにはアニメーションと物理挙動、両方をブレンドして動かす必要があります。そのためにはPhysicalAnimationコンポーネントが必要です。

PhysicalAnimationコンポーネントを追加

BP_HakochanにPhysicalAnimationコンポーネントを追加します。

f:id:nca03132:20201213213429p:plain

ブループリントに追記

PhysicalAnimationコンポーネントとSkeletalMeshコンポーネントの設定をBeginPlayに追記します。

まずはPhysicalAnimationコンポーネントのSetSkeletalMeshComponentを呼び出して、PhysicalAnimationコンポーネントとSkeletalMeshコンポーネントの関係性を設定します。 

f:id:nca03132:20201213215035p:plain

次に、SetSkeletalMeshComponentの後ろでPhysicalAnimationコンポーネントのApplyPhysicalAnimationSettingsBelowを呼び出して物理アニメーションプロファイル(物理とアニメーションのミックス設定)を指定します。

f:id:nca03132:20201213215411p:plain

引数は以下の通り

〇ApplyPhysicalAnimationSettingsBlow

  • Body Name
    物理とアニメーションをミックスするボーンの始点を設定します。
    ルートボーン名を指定しておいてください。
    それ以外を指定した場合、その先のボーンはアニメーションとブレンドされ、前のボーンは物理のみの挙動となります。
  • Physical Animation Data
    物理アニメーションプロファイルを指定します。
    詳しくはこの後に説明しているMakePhysicalAnimationDataを参照
  • Include Self
    チェックを外しておきます

〇MakePhysicalAnimationData

  • Is Local Simulation チェック入れる
    メッシュ内のローカル空間でのシミュレーションしたいのでチェックを入れます
  • Orientation Strength
    1000.0を指定します
  • Angular Velocity Strentgh
    100.0を設定します。

Orientation Strength、Angular Velocity Strentghで指定した数値は公式HPの推奨値で、まずこの値で動かしてみてそこから調整する形になります。

これで設定は完了です。

動かしてみると・・・

 f:id:nca03132:20201213221749p:plain

まぁ、相変わらずスクリーンショットだとわかりませんが、実際にはうにょうにょと蠢いています。

マネキンも同じような感じで設定して走りや歩きのアニメと合成させてレベルに配置してみると・・

f:id:nca03132:20201213222027p:plain

こんな感じで、くんずほぐれつ仲良く蠢いております。

 

 

ハード参照とソフト参照

勉強会とかでちらほらと、アセットのハード参照、ソフト参照って言葉が出てきますよね。
自分もぼや~っとは認識していたのですが、正確に違いや使い方を理解していなかったのでtwitterでポロっとつぶやいたところ、カニパンチさんとまめおさんが超絶丁寧に教えてくれました。多謝!
せっかくなので自分の備忘録と、これから同じ悩みを抱えるであろう方のため資料として、ブログを残しておくことにしました。
今回はキャラクターの武器付け替えを実装する際に発生するハード参照の問題を例にとり、ハード参照とソフト参照の違いについて説明していきたいと思います。

ハード参照

ハード参照による実装

まず武器の付け替えシステムですが、何も考えずに非常に簡単に組むとこうなります。
武器用にStaticMeshComponentを追加して
f:id:nca03132:20201030181854p:plain
1,2で武器メッシュを切り替えるようにする
f:id:nca03132:20201030182243p:plain
キャラクターの参照ビューアを確認すると、BPで参照している武器メッシュ(S_Sword,S_Katana)に対して白い線が結ばれているのがわかります。この白い線がアセットへのハード参照を表しています。
f:id:nca03132:20201030183347p:plain
この状態で1,2キーを押すと、きちんとキャラクターの武器メッシュが切り替わるのが確認できます。

ハード参照の問題点

何の問題もなく動いているように見えますが、この状態にはちょっとした落とし穴があります。
数字キーを押すことにより武器メッシュが切り替わるので、使用していない武器メッシュはアンロードされていそうに見えますが、ところがどっこいすべてオブジェクトとしてメモリにのった状態となっています。
これはUE4の仕様で、アセット読み込み時にはそのアセットがハード参照を持つアセットもすべて読み込むようになっているためです。
武器メッシュはさらにマテリアルやテクスチャのハード参照を持っているので、それらも全武器の分だけ丸っと全部読まれます。
付け替え武器が2~3種類なら全然許容範囲かもしれませんが、100とか200になってくると、キャラ生成時に全武器がメモリに乗ることになるので非常によろしくない状態になるのは容易に想像できると思います。

武器メッシュを要素に持つデータテーブルや配列を用意しても回避できない

いやいや100とか200の武器メッシュの切り替えをBPでべた書きするわけないでっしゃろ。
武器メッシュの参照を要素にもつデータテーブルを用意し、そこから読み込む武器メッシュを取り出してセットすればええやんけ……と思う方もいらっしゃると思います。
残念ながらこれもダメです。
試しに先ほどのS_Sword,S_Katanaを保持するデータテーブルを作ってみると
f:id:nca03132:20201030184500p:plain
データテーブルは要素にある全武器メッシュのハード参照を持つことになります。
f:id:nca03132:20201030184558p:plain
キャラクターはデータテーブルをメンバ変数に持つとデータテーブルのハード参照を持つようになります。
f:id:nca03132:20201030185013p:plain
従って、キャラクターはデータテーブルを介して全武器メッシュへのハード参照を持つことになり、やっぱりキャラクターロード時に全武器メッシュが読み込まれてしまうのです。
キャラクターにデータベースの代わりに全武器メッシュの配列やマップ変数を持たせても同じ結果になります。
とにかく!利用法はどうであれ参照ビューアで白い線(ハード参照)を持ったアセットは、全部芋ずる式にメモリに読み込まれてしまうことに注意してください。

ソフト参照

ソフト参照による実装

そこでソフト参照の登場となります。
ソフト参照はハード参照と同じように使用アセットを設定しておくことが出来ますが、親アセット生成時に自動的にロードはされません。
逆に言えば必要になった時には明示的にロードを行う必要があります。
まずは武器メッシュ用のソフト参照をキャラクターの変数として作成します。
キャラクターに新規変数を作成し、変数の型にStaticMeshのソフトオブジェクト参照変数を指定します。
f:id:nca03132:20201030185907p:plain
上記手順でSoftRef_Sword,SoftRef_Katanaという2つのソフトオブジェクト参照変数を作り一度コンパイルします。
f:id:nca03132:20201030190718p:plain
それぞれのソフトオブジェクト参照変数のデフォルト値にS_Sword、S_Katanaを設定しておきます。(※ここ忘れないように)
この状態で参照ビューアを確認すると武器メッシュへの線はピンクに変わっていることが確認できます。これがソフト参照です。
f:id:nca03132:20201030190822p:plain
あとは武器使用時にロードしてSetStaticMeshの引数に渡すようにします。
ソフト参照アセットのロードは同期、非同期の2通りの方法で行うことが出来きます。

同期読み込み

f:id:nca03132:20201030191348p:plain

非同期読み込み

f:id:nca03132:20201030191645p:plain

ソフト参照の問題点

なにより「使う前にはかならずロードする必要がある」という点でしょう。
ハード参照の説明の時に問題点として挙げた「武器メッシュを要素に持つデータテーブルや配列を用意しても回避できない」については、データテーブルや配列を武器メッシュへのソフト参照に変更し、使用前にロード処理を入れれば問題なくソフト参照で運用できます。
なお、ロードを書き忘れて以下のようにしてしまった場合でもエディタ上では動くことがあります。
f:id:nca03132:20201030193115p:plain
が、これはエディタが内部的にアセットを事前ロードしているためたまたま動いているだけであり、パッケージ化にしたときに動かずハマることになります。
アセットのロードが完了していることを保証できるならこれで問題ありませんが、そうでなければロード処理は必ず書きましょう。

意図せずハード参照を引き起こすケース

せっかく気を付けてソフト参照で書くようにしても、ふとしたことで意図せずハード参照を発生させてしまうことがあります。
以下に気を付けるべきケースをいくつが紹介します。

型の直接指定があるノード

冒頭で紹介したSetStaticMeshノードもそうなのですが、ノードの中にはダイレクトにアセットやクラスを指定できるものが存在します。
以下にいくつか例を挙げておきます。
f:id:nca03132:20201031083958p:plain
こういったものに直接アセットやクラス指定を入れるとハード参照が発生します。
クラス指定についてはブループリントではハード参照を避けて通れない(ソフト参照変数を用意してもハード参照になってしまう)ので設計を工夫するしかないのですが、データアセットについてはあとから付け替える可能性があるならソフト参照の導入を検討しましょう。
(※)C++を使えばClassを動的取得するノードを作ってクラス指定子につなぐなどで回避可能という情報をいただきました(カニパンチさんありがとうございます)

CastTo

CastToもハード参照を発生させてしまいます。
例えば敵に攻撃がヒットしたときに、敵の種類に応じてダメージを与える計算が変わる場合について考えてみましょう。
f:id:nca03132:20201030192017p:plain
アホの子みたいなBPですがサンプルとして勘弁していただくとして(汗)、ここではCastToがEnemy1,Enemy2,BossCharacterと3つ使われています。
純粋に処理を分けたいだけなのですが、実はこれだけでこのキャラクターはEnemy1、Enemy2、BossCharacterへのハード参照を持ってしまいます。
f:id:nca03132:20201030192403p:plain
ダンジョンのフロアごとにレベル分けして、1FにはEnemy1、2FにはEnemy2、3FにはBossCharacterしか登場しないとしても、プレイヤーがこのブループリントを使っている時点ですべてのフロアでEnemy1、Enemy2、BossCharacterのオブジェクトが生成されてしまいます。もちろんそれらに使われているリソースもロードされます。
さらにもし万が一、Enemy1がまた別のキャラクターへのハード参照で持っていると、それも数珠つなぎにロードされて・・と、この辺はかなり気を使わないと事故を引き起こす可能性があるので要注意です。
CastToは使った瞬間、問答無用にCast先クラスへのハード参照を作ってしまうのでインターフェースを切る・ディスパッチャを使う・不要なダウンキャストを避けるといったクラス間依存性を下げる設計を検討しましょう。

最後に

というわけで、得た知見をつらつらと書いてみましたがいかがでしたでしょうか。
今回は武器メッシュということでスタティックメッシュへの参照を扱いましたが、基本的にすべてのアセットは同じ理屈でロードされます。
作っているプロジェクトがある程度大きくなってきて多数のアイテム、装備、敵を扱うようになったら、各アセットを参照ビューアを確認して白で繋がってるアセットは全ロードされるということを念頭に正しい参照関係が構築できているか確認するとよいでしょう。
[2020/11/05追記]
ヘキサドライブさんが「BPの参照連鎖を断つ手法」というTIPSをブログにアップされました!
ハード参照を避ける手法について、より突っ込んで詳しく解説している神記事です。
直リンク張っていいのかわからなかったので紹介だけでもしておきます。
ヘキサドライブさんのHPから、研究室→プログラムTIPS→2020/11/5とたどれば見れると思いますので、ぜひ参考にしてみてください。

WorldComposition入門

ワールドコンポジション

ワールドコンポジションとは

複数のランドスケープを格子状に並べてオープンワールドで使われるような巨大なマップを構築する機能です。本記事はUE4.25での機能を紹介します。

個々のランドスケープはそれぞれサブレベルとして保存され、カメラとの距離によって自動的にストリーミング(読み込み、破棄)が行われます。

セットアップ

新規レベルの作成

まず新規にパーシスタントレベルを作り保存します。

ワールドコンポジションはパーシスタントレベルが保存されているフォルダ配下を自動検知してサブレベルを探し出します。

そのためワールドコンポジションを使う場合は専用のフォルダを用意したほうがよいでしょう。

今回はMapsにLandscapeフォルダを作り、その中にLandscape_Baseとして保存します。

f:id:nca03132:20200726163556p:plain

ワールドコンポジションの有効化

ワールドセッティングの「ワールド>ワールドコンポジションの有効化」にチェックを入れます。

f:id:nca03132:20200726163624p:plain

このとき

 「ワールドコンポジションは、このレベルが保存されているフォルダをスキャンしてサブレベルを自動検出するが問題ないか?」

という旨の確認が表示されるのでOKを押します。

すると、レベルウィンドウに「ワールドコンポジションを呼び出す」というボタンが追加されます。

このボタンはあとで何度も使うのでよく覚えておいてください。

f:id:nca03132:20200726163702p:plain

赤い線は右方向が正のX軸、緑の線は下方向が正のY軸で、中央の矢印マークはエディタ上での現在の視点を表したトップダウンビューになっています。

ビューポート上で視点移動すると矢印マークも移動することが確認できると思います。

最初のサブレベルの作成

ワールドコンポジションは、パーシスタントレベルが保存されたフォルダの配下にあるサブフォルダにサブレベルを配置する設計になっています。

現在、パーシスタントレベルはMaps>Landscapeに保存されているので、ここにSubLevelsというフォルダを作ります。

その中にレベルを新規作成し、名前をLandscape_x00_y00とします。

f:id:nca03132:20200726163738p:plain

x00,y00はワールドコンポジション上のグリッド座標を表すようにしました。

x,y両方0なので、原点位置のサブレベルとなります。

レベルを作成すると、レベルウィンドウのサブレベルに今作ったLandscape_x00_y00が自動的に追加されます(追加されてない場合はすべて保存をしたからパーシスタントレベルをリロードしてください)

f:id:nca03132:20200726163758p:plain

Landscape_x00_y00をダブルクリックして編集状態にします(自動的にレベルの可視性もONになります)。

f:id:nca03132:20200726163819p:plain

次に、ランドスケープモードにしてランドスケープを追加します。

f:id:nca03132:20200726163842p:plain

ランドスケープの作成設定で、新規作成、編集レイヤーを有効化にチェックを入れ、マテリアルに適用したいランドスケープマテリアルを指定、他はデフォルトで「作成」を押します。

f:id:nca03132:20200726164002p:plain

 これでLandscape_x00_y00に500mx500mのランドスケープが作成されました。

最初は真っ暗なのでランドスケープのペイントを選択し

f:id:nca03132:20200726164017p:plain

塗りつぶしたいレイヤーのレイヤー情報を選択します。(ない場合は作成してください)

f:id:nca03132:20200726164034p:plain

レイヤー情報を設定したら、右クリックしてレイヤーを塗りつぶすをクリックします。

f:id:nca03132:20200726164057p:plain

マテリアルが適用されました。

f:id:nca03132:20200726164121p:plain

 ワールドコンポジションウィンドウにも作成したランドスケープが表示されます。

f:id:nca03132:20200726164137p:plain

ランドスケープタイルをドラッグすると位置を移動させることができますが、事故防止のためにこの機能を無効化したい場合は右クリックメニューで「タイルの位置をロック」にチェックを入れておきます。

f:id:nca03132:20200726164154p:plain

次に、ランドスケープモードを終了してアウトライナを確認してみてください。

f:id:nca03132:20200726164208p:plain

この赤線のLandscape,LandscapeGizmoActiveActor,LevelBoundsの3つのアクターが存在するはずです。

たまにこれらがアウトライナ上に現れないことがあるので、その場合はすべてを保存しLandscape_Baseを開きなおしてください。その際、サブレベルはロードされないので手動でLandscape_x00_y00を読み込んでください(レベルウィンドウ上でサブレベルを右クリックしてロードを選択)

LevelBoundsアクターが存在しない状態で、次のサブレベルの追加を行うとサブレベルが同じ位置に重なって作成されてしまうので注意してください。

サブレベルの追加

最初に作ったサブレベルのタイルの横にさらにサブレベルのタイルを追加します。

ワールドコンポジションウィンドウで、最初に作ったタイルを右クリックし「隣接するランドスケープレベルを追加」を選択します。

-X,+X,-Y,+Yの選択肢が出てくるので、左に作るために-Xを選択します。

f:id:nca03132:20200726164233p:plain

保存するサブレベル名を聞かれるので、最初にサブレベルを保存したSubLevelsフォルダを指定して、「Landscape_x-01_y00」という名前で保存します。

f:id:nca03132:20200726164251p:plain

これで左側にLandscape_x-01_y00が追加され、レベルウィンドウとワールドコンポジションウィンドウに反映されます。

f:id:nca03132:20200726164308p:plain

ここで、アウトライナに再び注目します。

f:id:nca03132:20200726164355p:plain

新しく追加した「Landscape_x-01_y00」レベルに、LandscapeStreamingProxyというアクターが追加されています。

この詳細を見ると、Landscape Proxyという項目でLandscapeアクタに「Landscape」アクタが設定されていることが分かります。

f:id:nca03132:20200726164413p:plain

これが何の設定かというと、「Landscape_x-01_y00」は自分がロードされたとき、ここで指定したランドスケープを自動的に複製します、という機能を表しています。

ですので「Landscape_x-01_y00」では新規にランドスケープを配置する必要はなく「Landscape_x00_y00」で設置したLandscapeをそのまま使ってスカルプト・ウェイトのペイントを行うことができます。ワールドコンポジションはこの機能があるが故に、複数のサブレベルをまたいで連続的なランドスケープのスカルプト・ペイントが行えるようになっています。

試しにランドスケープのペイントモードで、再度ペイントレイヤーで塗りつぶしてみると

f:id:nca03132:20200726164430p:plain

「Landscape_x-01_y00」もウェイトペイントされることがわかります。

f:id:nca03132:20200726164455p:plain

2つのタイルを跨いだスカルプトも普通にできます。

f:id:nca03132:20200726164516p:plain

注意点として一旦パーシスタントレベルを閉じて再度開きなおすと、サブレベルが全く読み込まれていない状態になります。

単にサブレベルがロードされていないだけなので、レベルウィンドウから読み込みなしてください。

 

以上でサブレベルの追加は終わりです。

これを繰り返してタイル状に上下左右にサブレベルを広げていけば、オープンワールドで使うような巨大なランドスケープを作成できます。

サブレベルの階層化

サブレベルは、サブレベル同士で親子関係を作ることができます。

先ほど作成したLandscape_x00_y00、Landscape_x-01_y00を例にとると、レベルウィンドウ上でx-01_y00をx_00_y00にドラッグアンドドロップすると、x00_y00がx-01_y00の親になります。

f:id:nca03132:20200726164544p:plain

これによりワールドコンポジションウィンドウ上などでx00_y00のタイルを移動させると、x-01_y00のパネルも連動して移動するようになります。

ストリーミングレイヤー

ストリーミングレイヤーとは

ワールドコンポジションのサブレベル=ランドスケープタイルは、視点からの距離によりロード、アンロードする閾値を持っています。

レベルウィンドウのワールドコンポジションボタンを押して、ワールドコンポジションウィンドウを開いてみましょう。

f:id:nca03132:20200726164603p:plain

左上に「Uncategorized」と表記されている部分があり、マウスカーソルを合わせると「ストリーミング距離:50,000」と表示されます。

これがストリーミングレイヤーと呼ばれるもので、ストリーミングする距離の閾値を保持しています。

f:id:nca03132:20200726164625p:plain

ランドスケープタイルはデフォルトではすべて「Uncategorized」ストリーミングレイヤーに属しています。

ストリーミング距離は50,000なので、各タイルは視点が500m内に入るとロード・可視化され、それ以上離れるとアンロード・非表示になります。

現在のランドスケープタイルは一辺500mなので、この中でプレイする限りは両方とも常にロードされている状態になっています。

ストリーミングレイヤーの追加

ではストリーミングレイヤーを新規に追加してみましょう。

ストリーミング距離を250mに設定したストリーミングレイヤーを作ります。

「+」を押して名前を「250m」、ストリーミング距離を「25000」として、「作成」を押します。

f:id:nca03132:20200726164643p:plain

ストリーミングレイヤーの割り当て

作成したストリーミングレイヤーをランドスケープタイルに割り当てます。

ランドスケープタイルを二つ選び(Shiftを押しながら連続して選択か、マウスドラッグで矩形選択)、右クリックメニューを出しレイヤーに割り当てるで250mを選びます。

f:id:nca03132:20200726164701p:plain

これで設定完了です。

ランドスケープタイルにマウスをホバーさせると、設定されているストリーミングレイヤーが確認できます。

レイヤー名は250m、ストリーミング距離:25000になっていることが分かります。

f:id:nca03132:20200726164720p:plain

実際にゲームプレイで確認してみます。

ランドスケープがロードされたことが分かりやすいようにランドマーク的な高い山を作っておきます(赤い四角形の部分)

プレイヤーのスタートは黄色い丸の部分から始めます。

f:id:nca03132:20200726164737p:plain

一辺約500mのランドスケープタイルが横に2枚並んだ状態です。

黄色い丸からもうちょっと右方向に進めば、右側のランドスケープタイルまでの距離が250mを切りロードされるはずです。

プレイ開始すると、まだ高い山は見えていません。

f:id:nca03132:20200726164753p:plain

しばらく進んでいくと・・

f:id:nca03132:20200726164807p:plain

ストリーミングロードされ山が見えるようになりました。

なお、今回追加したストリーミングレイヤーは1つだけですが、複数追加して、それぞれのタイルに別の設定を割り当てることも可能です。

どこからでも見えるランドマーク的なタイルは遠め、ジャングルなど見通しが悪いタイルは短めといった感じでシチュエーションに応じて使い分けましょう。

ストリーミングレイヤーの割り当て状況確認

ワールドコンポジションウィンドウで、ストリーミングレイヤーのボタンをクリックすると、そのストリーミングレイヤーに割り当てられているタイルが表示されます。

250mをクリックするとタイルは両方とも表示されます。

f:id:nca03132:20200726164822p:plain

Uncategorizedをクリックするとタイルは表示は消えます。これは現在Uncategorizedに属しているタイルがないためです。

f:id:nca03132:20200726164836p:plain

Ctrlを押しながらストリーミングレイヤーをクリックすると複数選択ができます。この場合、それぞれに属しているタイルがすべて表示されます。

f:id:nca03132:20200726164851p:plain

ストリーミングレイヤーの削除

実は、不要になったストリーミングレイヤーを直接実行する方法はないのですが、Uncategorizedを除いてどこにも割り当てられていないストリーミングレイヤーはパーシスタントレベルをリロードしたときに自動的に削除される仕様となっています。

サブレベルへのアクター配置

これについては通常のサブレベルの注意点と同じです。

ワールドコンポジションにより自動的にサブレベルのロード、アンロードが行われるため、そのサブレベルに属しているアクターたちも連動して生成、消去されます。

該当するタイル上に静的な構造物を設置する場合は特に問題になりませんが、タイルを超えて移動するモンスターなどの配置は別管理にしたほうがよいでしょう。

UnrealEngine学習ポータルのラーニングパス

 

f:id:nca03132:20200504115716p:plain

UnrealEngineオンラインラーニングパス

ひさしぶりにオンラインラーニングパスを見たら、めちゃくちゃ進化してびっくりしました!

チュートリアル系の日本語化もかなり充実してきており、今ならイチから始める場合でもこの公式を追っていけば必要な技術は一通り揃いそうです。

ラーニングの進捗はダッシュボードで確認できるようになっており、コースが終了するとバッチがもらえる仕組みになってます。トロコンみたいで楽しいですよね。

 サイトは

こちらになりますので、今日がある方はレッツトライ!

お勧めコンテンツ

日本語翻訳されているコースの中で、ゲームを作るうえで特にお勧めなものをラーニング順にピックアップしておきます。

Unreal Engine 最初の 1 時間

Unreal Engine 最初の 1 時間 - Unreal Engine

文字通り最初にUnrealEngineを触り始めるときに最適なコースです。

 

プロジェクト作成、マーケットプレイスからのアセット導入、コンテンツブラウザからレベルへの配置といった体験ができます。

Unreal エディタの基本 - エディタ入門

Unreal エディタの基本 - エディタ入門 - Unreal Engine

「最初の一時間」より突っ込んだ、エディタの使い方の基礎について学ぶことができます。

Unreal Engine 入門

Unreal Engine 入門 - Unreal Engine

上記2つをまとめたものに近いですがエディタの環境設定やプロジェクト設定、ワールドセッティングといった部分についても言及されています。

ブループリントの基礎概念

ブループリントの基礎概念 - Unreal Engine

UnrealEngineでロジックの組み立ての基本となるブループリントに関するチュートリアルです。

マテリアルの基礎概念

マテリアルの基礎概念 - Unreal Engine

メッシュの表面に張り付けられる質感=マテリアルに関する説明です。

テクスチャの基礎や、テクスチャを使ったマテリアルの作成、質感の出し方、マテリアルアニメーションといった項目について解説されています。

マテリアルの制作ワークフロー

マテリアルの制作ワークフロー - Unreal Engine

マテリアルの基礎概念を発展させ、マテリアル関数を使った計算や、動的なパラメータを使ったパラメータ変更、レイヤー化されたマテリアルの扱いについて学びます。

マテリアル マスターラーニング

マテリアル マスターラーニング - Unreal Engine

マテリアルに関して上記2つをさらに進め、テクスチャの圧縮やパフォーマンス、マテリアル計算式の組み合わせから、HLSLなどのシェーダーにまで言及しています。

肌、髪の毛などのキャラクターの質感から草、岩、金属など様々な材質に関するマテリアルの作例に関する解説があります。

内容が高度なので、表現についてそこまでこだわらない場合はいったん飛ばしても問題ないかもしれません。

(以下、加筆中)

 

初めての UMG - Unreal Engine

初めてのシーケンサ - Unreal Engine

 ライティングの基礎概念とエフェクト - Unreal Engine

ポストプロセスの基礎 - Unreal Engine

ポストプロセス マテリアルを使用したレンダリングのスタイライズ - Unreal Engine

リアルタイムレンダリングの基礎 - Unreal Engine

リアルタイムレンダリングの詳細 - Unreal Engine

プラグイン作成と使用のベストプラクティス - Unreal Engine

探偵事務所のゲーム環境を作成する - Unreal Engine

プロジェクトのパッケージ化入門 - Unreal Engine

燃える木材マテリアルの作成 - Unreal Engine

シンプルな水マテリアルの作成方法 - Unreal Engine

物理ベースのショットビズ - Unreal Engine

PBR マテリアルの作成 - Unreal Engine

ブループリントを使用した AI の紹介 - Unreal Engine

グローバルイルミネーションの基礎 - Unreal Engine

プロジェクトとファイル構造について - Unreal Engine

プロシージャルジオメトリに基づいた雲の作成 - Unreal Engine

テクスチャのブレンドとタイリング - Unreal Engine

 

VR ウォークスルーの制作 - Unreal Engine

 

Datasmith をはじめよう - Unreal Engine

Datasmith パイプライン入門 - Unreal Engine

SketchUp から Unreal への入門 - Unreal Engine

Revit から Unreal Engine に関する基礎 - Unreal Engine

AEC 向けサンプルによるブループリント - Unreal Engine

 

建築インテリア リアルタイム プロジェクトの作成 - Unreal Engine

建築エクステリア リアルタイム プロジェクトの作成 - Unreal Engine

UINavitagion入門

f:id:nca03132:20200427133029p:plain

UINavitagionとは

UINavigationはマウス、キーボード、ゲームパッド、またはそれらの任意の組み合わせで操作できるUMGメニューを構築するためのフレームワークで、なんとオープンソースで無料でマーケットプレイスで入手できます!

自分でイチからUMGのゲームパッド、マウス・キーボード両対応を行おうとすると結構大変なので、その辺を楽に共通化したいと思ってる方には非常に有用なプラグインです。

ただ、通常のUMGにはないNavigationGridという概念をベースに機能拡張がなされているため、使いこなすにはよく仕様を理解しておく必要があります。

本家チュートリアル

UI Navigation Tutorials - YouTube

こちらになっており一通り見ておけば使えるようになるのですが、英語動画なのでちょっとハードルが高いかも……ということで自分への備忘録も兼ねて記事として残しておきます。

プラグインの有効化

なにはなくとも、まずプラグインを有効化しましょう。

対象となるプロジェクトで"編集">"プラグイン"と選んで

f:id:nca03132:20200426125750p:plain

UINavigationにチェックを入れて有効化しエディタを再起動します。

f:id:nca03132:20200426125850p:plain

 

インプットアクションの設定

まずはUINavigationで使うインプットアクションをプロジェクトに設定する必要があります。UIで設定するのは大変なので、プラグインのinput設定をプロジェクトにコピペしましょう。コンフィグを書き換えるためUE4エディタはいったん終了しておいてください。

UnrealEngineをインストールしたディレクトリ配下の"Plugins">"Marketplace">"UINavigation">"Content"に"UINavInput.ini"というファイルがあるのでテキストエディタなどで開きます。

f:id:nca03132:20200426130435p:plain

ファイル中でActionMappingsを設定している部分をコピーし

f:id:nca03132:20200426130738p:plain

対象となるプロジェクトのConfig/DefaultInput.iniを開いて、ActionMapping設定している部分にペーストします。

f:id:nca03132:20200426131136p:plain

これでアクションの登録は終了です。

ウィジェットの作成

UINavigationではウィジェットはUserWidgetではなくUINavWidgetを親クラスにする必要があります。

コンテンツブラウザ上で普通にウィジェットを作ったあとで、"グラフ"、"クラス設定"をクリックし、クラスオプションで親クラスをUINavWidgetに変更します。

f:id:nca03132:20200426134723p:plain

UINavWidgetはUserWidgetの派生クラスで同等の機能を持っているため、このリペアレントは問題なく実行できるはずです。

またゲームパッドやキーボードによるボタンの制御を有効化するために"クラスのデフォルト"をクリックし詳細の"UINavWidget"の中にある"User Button States"にチェックを入れてください(これを忘れるとキーボードやゲームパッドでボタンを操作できないので注意)。

f:id:nca03132:20200426140054p:plain

"Use Text Color"は必要に応じてチェックしてください。この機能は、キーボードやパッドで現在選択されているボタンの文字色を変更します。文字色は"UINavitgationText"の項目で変更することができます。

今回の解説ではカーソル移動をわかりやすくするためにUseTextCursorはチェックした状態で進めていきます。

 ウィジェットへのボタン配置

UINavigationではボタンは通常のButtonではなく、UINavButtonを使用します。

CanvasPanel上に普通においてもいいでし、VerticalBox,HorizontalBoxなどのパネルに入れても大丈夫です。

f:id:nca03132:20200426141208p:plain

これはCanvasPanel上にVerticalBoxを置いてUINavButtonを3つ配置した例です。

UINavButtonはIsFocusableのチェックを外したままにしておいてください(UINavigatonによるセレクション移動と標準のNavigationによるキーボードフォーカス移動が混在して動いてしまうため) 

NavigationGridの作成

次にUINavigationのキモであるNavigationGridを作成します。

NavigationGridとは

キーボードやゲームパッドでUMGを操作する時は上下左右のカーソル入力で選択を移動させますが、NavigationGridというのはこの移動範囲を規定するものになります。

例えばVerticalBoxで縦方向に3個ボタンを並べたときは、縦方向のNavigationGridを生成し中にその3ボタンの参照を入れておきます。

GridPanelにボタンが入っている場合は縦横方向にカーソルが移動するので、2次元のNavigationGridを生成し中にそのボタンの参照を入れておきます。

ゲーム実行時、UINavigationはカーソルをこのNavigationGrid内で移動させます。

NavigationGridを作成する

NavigationGridはウィジェットのグラフ上でReadyForSetupイベントからAppendNavigationGrid1D、AppendNavigationGrid2Dいずれかのノードを呼び出すことにより作成します(これ以外のイベントからは呼び出さないように)。

上記の2つのノードは内部的にNavigationGridを生成してウィジェットに配置されたボタンを階層の上から順に引数で指定された数だけグリッドにいれます。

 AppendNavigationGrid1D

ボタンが縦方向、もしくは横方向に一列に並んでいるときに使います。GridTypeはレイアウトに応じてHorizontalかVerticalから選択してください(Grid2Dを指定するとエラーになる)。

引数のDimensionはNavigationGridに挿入するボタンの数を指定します。

f:id:nca03132:20200427133358p:plain

このようにVerticalBoxにボタンを配置した場合は

f:id:nca03132:20200426151218p:plain

このようにノードを組んでVerticalBoxに積まれたボタンをすべていれます。

AppendNavigationGrid2D

UniformGridPanelのように縦横両方にカーソル移動できる配置で使われます。横と縦の次元数をDimensionX,DimensionYで指定します。

グリッド内のボタンの数がDimensionX×DimensinYより少ない場合、Buttons in Gridにボタンの数を直接設定して調整してください(ピッタリの場合は-1のままでよい)。

f:id:nca03132:20200426153300p:plain

UniformPanelでこのように配置した場合は

f:id:nca03132:20200426153326p:plain

このようにDimensionXを3,DimensionYを2にします。

この例ではボタンは1個足りないのでButtonInGridで5に指定する必要があります。

プレイヤーコントローラーの設定

プレイヤーコントローラーの作成

UI NavigationではプレイヤーコントローラーをUINavControllerを派生したものにするか、コンポーネントにUINavPCを追加し更にインターフェースとしてUINavPCReceiverを継承させたものに変更する必要があります。

派生させる場合は、プレイヤーコントローラーのBPを開いて"クラス設定"をクリックし、詳細で親クラスを"UINavController"を選択します。

 f:id:nca03132:20200426173209p:plain

コンポーネントを追加する場合、プレイヤーコントローラーに"UNavPC"コンポーネントを追加し、"クラス設定"をクリックし、実装インターフェースで"追加"を押して"UINav PCReceiver"を追加します。

f:id:nca03132:20200426173652p:plain

プレイヤーコントローラーを作成したら、それが使われるようにゲームモードのプレイヤーコントローラー指定を書き換えておいてください。

f:id:nca03132:20200426174857p:plain

動作確認

ここで実際に動作確認してみます。

今回の例ではサードパーソンのテンプレートをベースにしています。

プラグインの有効化、インプットアクションの設定を終えた後

この4つの手順を踏んでください。

今回はWBP_Sampleというウィジェットを作成しVerticalBoxに3つのUINavButtonを配置し、AppendNavigationGrid1DでVerticalにNavigationGridを生成した場合の動作を見ていきます。

ウィジェットの見た目はこんな感じで

f:id:nca03132:20200427081718p:plain

グラフは

f:id:nca03132:20200427081851p:plain

こうなります。

次に先ほどUINavControllerを派生して作ったプレイヤーコントローラーにウィジェットを表示するノードを追記していきます。

シンプルに表示するだけなのでBeginPlayにウィジェットの作成、ビューポートへの追加、マウスカーソル表示ONとつなぎます。

f:id:nca03132:20200427082305p:plain

さらに、プレイヤーの移動、視点回転の動きを抑制するノードを追加。

f:id:nca03132:20200427082627p:plain

最後に入力モードをGameAndUIに設定します。

f:id:nca03132:20200427082653p:plain

InputModeはUIOnlyでも動くことは動くのですが、UMGが表示されてない個所をマウスクリックしてしまうとウィジェット選択が切れてゲームパッドやキーボードでUMGが操作できなくなってしまうためお勧めしません(一応、マウスで再度UMGのどこかをクリックすると復帰します)。

またSetIgnoreMoveInput、SetIgnoreLookInputでキャラや視点の移動を防いではいますが、実際のゲームではこのほかにも攻撃ボタン、アイテム使用ボタンなど多数の入力が存在すると思います。

もしメニューを開いている間はそういった入力を受け付けないようにしたい場合は、メニューを開いているフラグなどを用意して自前でプレイヤーがアクションしないように制御してあげる必要がある点に注意してください。 

これで設定は完了です。ちゃんと設定できていれば

f:id:nca03132:20200427084049p:plain

 こんな感じの画面になるかと思います。マウスでボタンを直接クリックすることもできますしキーボード(ADSW)やゲームパッドでカーソルを移動、決定させることも可能です。

複数のNavigationGrid

NavigationGridを複数生成する

先ほどの例ではNavigationGridを1つだけ生成しましたが、当然複数生成することもできます。VerticalGridのほかに3x2のUniformGridPanelを追加しボタンを5個追加してみます。

f:id:nca03132:20200427090319p:plain

これでパネルは追加されましたが、NavigationGridはVerticalBoxの分しか作っていません。UniformedGrid用にもNavigationGridが必要なのでAppendNavigationGrid2Dで追加します(UniformedGridは上下左右にカーソルが動くので)。

ウィジェットのグラフは

f:id:nca03132:20200427091211p:plain

こんな感じになります。これでNavigationGridが2つになりました。

ここで補足ですがAppendNavigationGrid系ノードはウィジェットの階層上のボタンを上から順にNavigationGridに追加していきます。今回の例では

f:id:nca03132:20200427092153p:plain

という順にボタンが並んでいるので、最初のAppendNavigationGrid1DでVerticalBoxに入っている3個のボタン、AddNavigationGrid2DでUniformGridPanelに入っている5個のボタンを各自生成したNavigationGridに追加します。この順序が逆になるとパネルの構成とNavigationGridの構成がズレてしまうので注意してください。

この状態で実行してみると

f:id:nca03132:20200427091741p:plain

ゲームパッドやキーボードでカーソルを動かすとVerticalBox内はカーソル移動できますがUniformGridPanel側にはカーソル移動しません。

UniformGridPanel側のボタンをマウスでクリックすると今度はUniformGridPanel内はカーソル移動できるようになりますが、やはりVerticalBox側にカーソルを戻すことはできません。

そもそもカーソルは1つのNavigationGrid内で動くように設計されているので正しい動作ではあるのですが、このままではちょっと困ります。そこで次で紹介するEdgeNavigationを使用します。

EdgeNavigation

NavigationGridにはカーソルをGridの外に動かそうとしたときに、指定したボタンに強制的にカーソルをジャンプさせるEdgeNavigationという設定があります。とび先には別のNavigationGridのボタンも指定できます。

EdgeNavitagionはAppendNavigationGridの引数として与えることができます。

f:id:nca03132:20200427124213p:plain

EdgeNavigationのピンをドラッグ&ドロップして"作成"と入力すると"ButtonNavitagionを作成"という候補が出てくるので選択します。

EdgeNavigation構造体を作成するノードが出来るので、カーソルがVerticalBoxのNavigationGridから外に出ようとしたとき、上下左右どの方向に出た時にどのボタンにジャンプするかを設定します。

f:id:nca03132:20200427125225p:plain

今回の場合、カーソルがVerticalBoxのNavigationGridから右方向へ出ようとしたらボタン4へ、UniformGridPanelのNavigationGridから左方向へ出ようとしたらボタン1へジャンプするようにします。f:id:nca03132:20200427130152p:plain

コンパイルして実行してみましょう。マウスを使わなくてもVerticalBoxとUniformGridPanelでカーソルが行き来できるようになっているかと思います。

NavigationGridの仕様

NavigationGridは単純にボタンのリストを保持していて、その中で指定されたグリッド形状に合わせてカーソルを動かしているだけなので、UMG上のパネルやグリッドのレイアウト・親子関係とは無関係に動作します。例えば上記の例でNavigationGridを最初のVerticalBoxの1つしか作成せずDimensionに8を設定するとVerticalBox内の3ボタン、UniformGridPanel内の5ボタンすべてが縦に並んでいるとみなして上下入力でカーソルが動きます。

f:id:nca03132:20200427131258p:plain

が、そんなことをすれば当然操作系は混乱するので、極力レイアウト上の配置と操作系はマッチするように運用したほうがよいでしょう。

またUINavigationは配置したボタンの数とNavigationGridに挿入したボタンの数が違うとエラーを出すので、きちっとボタンの配置数は把握してNavigationGridを作成してください。

MenuSelector

MenuSelectorはカーソルがあるボタンに対して表示するマーカー的なものです。

例えば選択中のボタンの左横に">"マークを出したり、縁取りで飾りを表示したりする場合に使います。

MenuSelectorは通常のウィジェットとして作成します。UINavWidgetではなく普通のUserWidgetで構いません。今回はサンプルに入っているMenuSelectorを使います。

UMGのデザイナー上でキャンバスにMenuSelectorを配置し、名称を"TheSelector"に変更します。("TheSelector"への名称変更はUINavigationの仕様です)

デフォルトでは"<"という形状になっているのでレンダートランスフォームのScaleで-1を指定して左右反転し、適当なボタンの左横に配置して大きさを変更しながらサイズ感を確認します。

f:id:nca03132:20200427143714p:plain

次にMenuSelectorをボタンの左側に表示されるように設定を行います。

"グラフ"をクリックし、"クラスのデフォルト"を選択して"UINavigationSelector"の中の"SelectorPositioning"を"Left"に指定し、にSelectorOffsetのxを-50を設定します(これはMenuSelectorのサイズに合わせて変更してください)。

f:id:nca03132:20200427144338p:plain

これで選択中のボタンの左に">"マークが表示されるようになるはずです。

f:id:nca03132:20200427144618p:plain

基本的な使い方は以上です。

あとは表示するMenuSelectorを変更したり、表示位置、オフセットなどを調整するなど自由にカスタマイズしてください。

ボタンにアニメーションをつける

UINavWidgetは配置中の各ボタンに対しカーソルが移動してきたときに再生するUMGアニメーションを"UINavAnimations"という配列で保持しています。

初期状態でこの配列は空ですが、ウィジェットのPreConstructイベントで各ボタンごとにUMGアニメーションを設定しておけばボタンにカーソルが移動してきたときに対応するアニメーションを自動的に再生してくれます。

先ほどのVerticalBoxに3ボタン、UniformGridPanelに5ボタン設置してある例だと、各ボタンごとに計8個のアニメーションを用意し、PreConstructイベントで"UINavAnimations"配列に設定します。

f:id:nca03132:20200427154007p:plain

これでボタンにカーソルが移った時に各アニメーションが再生されます。

本家動画では

https://www.youtube.com/watch?v=lZjR2ogcBt8&feature=youtu.be&list=PLAcWSem_HT4hT-3viMU2_z4WW94gKhgn0&t=80

この辺の操作を参考にするとよいでしょう。(動画ではボタンは6個です)

UINavComponents

ここまでの記事ではカーソル移動といえばボタン(UINavButton)に対するものとして話を進めてきましたが、実はUINavigationにおいてカーソル移動できるパーツはUINavComponentというクラスが基本になっています。

UINavButtonはこの派生クラス(一種)で、ほかにもカーソル移動を受け付けられるパーツがいくつか存在します。

コンテンツブラウザの表示オプションで"エンジンのコンテンツを表示"、"プラグインコンテンツを表示"にチェックを入れ、プラグインのフォルダを表示状態にすると

f:id:nca03132:20200427162107p:plain

UINavigationコンテンツ>Examples>Components

の中を見ることができるようになります。

この中には各種UINavComponentの実装サンプルが入ってます。

f:id:nca03132:20200427162457p:plain

これらのパーツもキーボードやゲームパッドでのカーソル移動を受けることができるものです。

この実装サンプルをそのままUMGデザイナーに配置して使ってもいいですし、プロジェクト側にコピーしてデザインを変更したり実装をカスタムして使うこともできます。

自分で実装を書く場合、UINavComponentはOnNavigatedToとOnNavigatedFromというイベントをオーバーライドできるので、ナビゲートを受けた時・出ていく時の処理を書き加えることができます。

以下、用意されているUINavComponentをいくつかを紹介します。

OptionBox(クラス名:UINavOptionBox)

f:id:nca03132:20200427172343p:plain

f:id:nca03132:20200427172539p:plain

ボタンの中にテキストと左右ボタンがついていて、中のテキストや数値変更が可能です。

数値を切り替える場合、UserNumberRangeにチェックを入れMinRange,MaxRange,Intervalにそれぞれ数値の最小値、最大値、左右ボタンを押したときの増減幅を設定します。

f:id:nca03132:20200427172658p:plain

テキストを切り替える場合は、UserNumberRangeのチェックを外してStringOptionsに切り替える文字列を追加します。

f:id:nca03132:20200427172832p:plain

どちらの場合もUINavComponentBox>LoopOptionsにチェックを入れると、最大値、最小値まで行ったときに値がループするようになります。

OptionBoxはGridTypeがHorizontalのNavigationGridに入れると、カーソルがOptionBoxに移ったあと左右キーが数値・文字列の切り替えに食われてカーソル移動できなくなります。VerticalなGridに入れて運用したほうがよいでしょう。

OptionBoxの左右ボタンやテキストのデザインを変更したい場合は、自分のプロジェクトにOptionBoxをコピーするか、UINavOptionBoxの子クラスを作ってカスタマイズする必要があります。

SliderBox(クラス名:UINavSliderBox)

f:id:nca03132:20200427173850p:plain

OptionBoxと似ていますが数値しか指定できません。その代わりスライダーバーが表示されます(クリックしての値変更はできない)。

プロパティやカスタム方法についてはOptionBoxの数値指定したときと同じです。

Slider(クラス名:UINavSlider)

f:id:nca03132:20200427174214p:plain

いわゆるスライダーバーですが、スピンボックスがついてます。

プロパティやカスタムの方法はSliderBoxとほぼ同じです。

こちらはマウス操作の時バーをクリックすることで数値を変更することができます。

配置初期状態ではHandleHoverColor(バー上に表示される現在の値を示すハンドル)、BarHoverColor(バーの色)のアルファ値が0でバーが表示されないので注意してください(色指定してください)。

InputContainer

f:id:nca03132:20200427234109p:plain

InputContainerはゲームパッドやキーボードのコンフィグを表示・設定・保存するための特殊なUINavComponentです。これを使うと簡単にキー・パッドコンフィグ画面が作れます。

データテーブルの設定

InputContainerはその機能上、画面にゲームパッド、キーボード、マウスの各種操作(Aボタン、スペースキー、マウス左クリックなど)をアイコンや名称で表示します。そのためゲームパッド・キーボード・マウスの各操作に対して、どのアイコン、どのテキストを表示するかを規定したデータテーブルが必要となります。

UINavigationにはこのデータテーブルのサンプルが用意されているのでそれを使えば新規に用意する必要はありません。カスタムする場合もこのサンプルをコピーして書き換える形になります。
※サンプルはコンテンツブラウザの表示オプションで"エンジンのコンテンツを表示","プラグインコンテンツを表示"の2つにチェックを入れた状態でないと表示されないことに注意してください。

では実際にデータテーブルを設定してみましょう。

プレイヤーコントローラーのUINavPCコンポーネントの詳細で設定を行います。UINavControllerカテゴリに以下のようにデータテーブルの設定が並んでいるので以下に従って指定してください。

f:id:nca03132:20200427192131p:plain

  • GamepadKeyIconData
    ゲームパッド操作と対応するアイコンファイルが記述されているデータテーブルです。
    テーブルで使われている構造体はInputIconMappingです。
    標準でXBoxIconTableというデータテーブルが提供されています。
  • KeyboardMouseKeyIconData
    キーボード・マウス操作とアイコンファイルが記述されているデータテーブルです。
    テーブルで使われている構造体はInputIconMappingです。
    標準でKeyboardMouseIconTableというデータテーブルが提供されています。
  • GamepadKeyNameData
    ゲームパッド操作と対応する名称が記述されているデータテーブルです。
    テーブルで使われている構造体はInputNameMappingです。
    標準でXBoxNameTableというデータテーブルが提供されています。
  • KeyboardMouseKeyNameData
    キーボード・マウス操作と対応する名称が記述されているデータテーブルです。
    テーブルで使われている構造体はInputNameMappingです。
    標準でKeyboardMouseNameTableというデータテーブルが提供されています。

これらを設定すると

f:id:nca03132:20200427192417p:plain

このようになります。

補足として、アイコンやキー名称を独自に書き換える方法も紹介しておきます。

基本的にはサンプルのデータテーブルを自分のプロジェクトにコピーして必要に応じて書き換えてください。

データテーブルはキー(RowName)がUE4のエンジンで規定されている操作名になっていて、データ列はアイコンテーブルの場合アイコンファイル、ネームテーブルの場合TEXT型の操作名になっています。

これはゲームパッドのアイコンテーブルの一部です。

f:id:nca03132:20200427183937p:plain

Rowには操作名、InputIconにはアイコンファイルのパスが入っています。

もしアイコンを変更したい場合はInputIcon列のアイコンファイルを変更してください。
こっちはゲームパッドのネームテーブルです。

f:id:nca03132:20200427184126p:plain

一行も入ってないですね。実はこれには理由があります。

各操作を画面に表示する際はアイコンデータテーブルのほうが優先的に使用されるので、そちらですべて対応できていればネームテーブルは参照されません。そのためこちらは空になっています。もし特定の操作に対してアイコンではなく名称で表記したい場合はアイコンテーブル側の行を削除し、ネームテーブル側に該当する操作について追記します。

アイコンテーブルもネームテーブルもヒットしなかった場合はエンジンで規定されているディスプレイネームが表示されます。

 プロジェクト側にデータテーブルをコピーして書き換えた場合はUINavPCコンポーネントのデータテーブル指定も書き換えることを忘れないようにしてください。

InputContainerの配置

ではUINavWidget内にInputContainerを配置してみましょう。

今回は単純にCanvasPanel上に直接置いてみます。

f:id:nca03132:20200427222908p:plain

InputContainer詳細で以下のプロパティを設定します。

f:id:nca03132:20200427223217p:plain

  • InputNames
    ここにはキーコンフィグしたいアクションマッピング名を追加していきます(プロジェクト設定のインプットに記述してあるアクションマッピング(Jumpなど))。
    複数指定可能で、その数だけInputContainerの行が増えます。
    今回は以下のような感じでJumpとAttackというアクションを指定してみます。

    f:id:nca03132:20200427223623p:plain

  • InputRistrictions
    InputContainerは各アクションに割り当てるキーを最大3個まで列に表示して確認・設定できる機能を持っています。
    InputRistrictionsではその列ごとに、操作に関して制限なし(None)、キーボード(Keyboard)のみ、マウスのみ(Mouse)、キーボード&マウスのみ(Keyboard and Mouse)、ゲームパッドのみ(Gamepad)の制限を設定することができます。
    今回は1列目はキーボードのみ、2列目はゲームパッドのみのキー割り当てを表示・変更できるようにします。

    f:id:nca03132:20200427233836p:plain

  • EmptyKeyText
    アクションに対して操作が何もバインドされてないときに表示するテキストを指定します。デフォルトではUnboundになっているので必要に応じて変更してください。

    f:id:nca03132:20200427233913p:plain

  • PressKeyText
    操作変更ボタンを押して入力待ちの間に表示されるテキストなので適切なテキストを設定してください。デフォルトではPressAnyKeyとなっています。

NavigationGridの生成

次にウィジェットのグラフでNavigationGridを生成します。

InputContainerはそれ単体でNavigationGrid2Dを1つ占有するのでReadyForSetupイベントからAppendNavigationGrid2Dを呼び出して2D上のグリッドを作成します。

DimensionXには各アクションに対して設定可能な操作の数を指定します。

これはInputContainer>KeysPerInputで取得することができます。

DimensionYには設定可能なアクション数を指定します。

これはInputContainer>NumberOfInputsで取得することができます。

ノードとしては

f:id:nca03132:20200427234637p:plain

のようになります。

動作検証

では実際にプレイしてみます。

画面上には

f:id:nca03132:20200427234849p:plain

このようにJump、Attackに割り当てられているキー操作、パッド操作が表示されます。

ゲームパッドやADSWでカーソル移動が可能で決定ボタンを押すとアクション変更状態となり入力待ちに入り、次に押された操作がそのアクションに割り当てられます。

設定の変更を行うと自動的に保存され次回以降のゲーム起動でも有効となることに注意してください。

キーバインドのリセット

以上で説明は終わりなのですが、ゲームパッドやキーボードのコンフィグは時としてリセットしたくなることがあります。

そんなときはResetInputSettingsノードを使えばすべてのコンフィグはリセットされます。

UINavCollections

UINavWidgetはネスト出来ないという大きな制限があり、別で作成したUINavWidgetを子として貼り付けようとするとエラーになってしまいます。

ネストしなければUINavWidgetを同時に複数ビューポートにアタッチすることはできますが、その場合カーソルは常に最後にAddViewportしたウィジェットに行ってしまい、手前のウィジェットに移動させることができません(ボタンをマウスで直接クリックすることは可能)。

UINavigationはUINavWidget1つですべてのナビゲーションをコントロールする設計で作られているので当然といえば当然なのですが、子ウィジェットが使えないというのは非常に使い勝手が悪いです。

そこで登場するのがUINavCollectionです。

UINavCollectionは普通のウィジェットのように様々なパーツを自由に設置でき、かつUINavWidgetの子に入れることができます。UINavCollection内にUINavigationをネストで入れることも可能です。

UINavCollectionを作成する

実際にUINavCollectionを作ってみます。

通常のウィジェットと同じようにユーザーインターフェースを作成したあと、親クラスをUINavCollectionに変更します。

"グラフ"をクリックし"クラス設定"を選択し、クラスオプションで"UINavCollection"を指定してください。

f:id:nca03132:20200428081050p:plain

あとは普通のウィジェットと同じように各種パーツを自由にレイアウトします。

今回はUniformGridPanelを下地にしてボタンを6個を配置してみます。

f:id:nca03132:20200428082004p:plain

NavigationGridを作成する

UINavCollectionでも配置したパーツに対してNavigationGridを作る必要があります。

UINavWidgetではReadyForSetupイベントでNavigationGridを生成していましたがUINavCollectionではSetupNavigationイベントを使います。

今回の例では3x2のUniformGridPanelなのでAppendNavigationGrid2Dで以下のようにノードをつないでグリッドを作ります。

f:id:nca03132:20200428091532p:plain

UINavWidgetに配置する

次にUINavWidgetに上記で作ったUINavCollectionを配置します。

左右に2つほど並べてみます。

f:id:nca03132:20200428100312p:plain

UINavWidgetでNavigationGridを作成する

UINavWidgetにボタンなどのUINavComponentを配置した場合はAppendNavigationGridでNavigationGridを生成しますが、UINavCollectionを配置した場合はAppendCollectionというノードを使います。

AppendCollectionノードは内部で配置されたUINavCollectionに対してSetupNavigationを呼び出すので、そこで子ウィジェット側のNavigationGridが作成される仕組みになっています。(UINavWidgetは配置されたUINavCollectionの内部構成について感知しないのでNavigationGridの生成をUINavCollectionに任せるのは至極正しい設計です)

今回の例ではUINavCollectionを2つ配置しているので

f:id:nca03132:20200428102048p:plain

こんな感じでAppendCollectionを2回呼び出します。

引数のEdge Navigationには何かしらピンを結んでおく必要があるため空配列を作って渡していますが、UINavCollection側のSetupNavigationイベントにそのまま渡される値なので、必要であれば設定してUINavCollectionに渡してあげるとよいでしょう。

実行してみると、EdgeNavigationを設定してないのでパネルの行き来はできませんが、それぞれのパネル内では正常にカーソル移動できることが確認できると思います。

f:id:nca03132:20200428104532p:plain

 

EdgeNavigationを設定する

最後にUINavWidgetでEdgeNavitagionの設定を行います。

便宜上、上記のUINavCollectionのうち、左側をCollection1、右側をCollecton2とすると

  • Collection1のNavigationGridのEdgeNavigationのRightをCollection2の最初のボタンにつなぐ
  • Collection2のNavigationGridのEdgeNavigationのLeftをCollection1の最初のボタンにつなぐ

この2つの処理が必要です。

AppendCollectionは引数でEdgeNavitaionを渡すことができますがCollection1を生成するときにEdgeNavigationにCollection2のボタンを設定しようと思っても、Collection2のNavigationGridがまだ生成されてないのでボタンを取得することができません。

ですのでCollection1、Collection2両方ともAppendCollectionでNavigationGridを生成したあとにEdgeNavigationを設定します。

f:id:nca03132:20200428114311p:plain

まずCollectioin1、Collection2に設定されたNavigationGridを取得する必要があります。

UINavWidgetは生成したNavigationGridsという配列に生成されたすべてのNavigationGridをを保持しているのでこれを使いましょう。

Collection1、Collection2に何番目のNavigationGridから割り当てられているかはGetFirstGridIndexで調べることができます。

最初にそれらを変数化して保存します。

f:id:nca03132:20200428114720p:plain

これでCollection1に設定されているNavigationGridは

f:id:nca03132:20200428115039p:plain

という形で取得できるようになりました。この時のGetはRefを使用することに注意してください。

f:id:nca03132:20200428115203p:plain

次に、このNavigationGridのEdgeNavigationを設定します。

Getの右のピンを引っ張って"メンバ"と入力すると"Gridにメンバを設定"という候補が出るので選択します。

f:id:nca03132:20200428123431p:plain

生成された"Gridにメンバを設定"ノードを選択すると詳細のPin Optionsに設定するメンバを選択するチェックボックスが表示されるので"Edge Navigation"にチェックを入れます。

f:id:nca03132:20200428123801p:plain

すると"Edge Navigation"のピンが追加されるので、そこから引っ張って"ButtonNavigationを作成"ノードを作ります。

これでやっとCollection1のEdgeNavigationを設定する準備が整いました。

f:id:nca03132:20200428124211p:plain

次はこのRightButtonにCollection2の最初のボタンをつなぎます。

Collection1と同じ方法でCollection2のNavigationGridを取得し、そこからピンを伸ばして"Gridを分解"を選択すると取得できるパラメータが展開されます。

f:id:nca03132:20200428124750p:plain

この中の"First Button"がCollection2が保持している最初のボタンです。

これを先ほどのCollection1のNavigationGridの"RightButton"につないでくだい。

"Gridを分解"のノードは縦に大きいのでクリックして詳細に表示される"Hide UnconnectedPins"のボタンを押してノードを縮めます。

"ButtonNavigaionを作成"のノードも縦に長いのでクリックして詳細に表示されるチェックボックスでつながっているRightButton以外のチェックを外します。

結果、上記の処理はこのようなノードにまとまります。

f:id:nca03132:20200428125415p:plain

これでCollection1からCollection2へのナビゲーションは設定されました。

同様の手順でCollection2からCollection1へのナビゲーションを設定するノードを構築すると

f:id:nca03132:20200428125838p:plain

このようになります。

長いですが全体だと

f:id:nca03132:20200428125949p:plain

このようになります。

実際に実行して正しくEdgeNavigationが動作することを確認してください。

ウィジェットのランタイム生成

アイテムウィジェットなどで、動的にボタンなどのUINavComponentをグリッド内に追加していきたい、といったシチュエーションがあるかと思います。

ここではその方法について紹介したいと思います。

パネルの準備

まずはUMGデザイナーで動的にボタンを追加するパネルを作ります。

今回はVerticalBoxを用意します。VerticalBoxはグラフでも使用するのでIsVariableにチェックを入れておきましょう。

f:id:nca03132:20200428164224p:plain

 

NavigationGridの作成

次にNavigationGridを作成します。

今までの静的なウィジェットではボタンなどを配置してからNavigationGridを生成していましたが、今回は動的操作なので先にNavigationGridを生成してしまいます。

UINavWidget上のReadyForSetupイベントでAppendNavigationGrid系のノードを呼び出してください。ポイントとして、この時Dimensionは0を指定して中身を空にしておきます。

今回の例ではVertialBoxなのでNavigationGrid1DでVertial指定しておきます。

f:id:nca03132:20200428165826p:plain

追加するUINavComponentの作成

パネルに追加していくUINavComponentの派生クラスを作成します。アイテムメニューだと1アイテムを表すボタンに該当するパーツです。

プラグイン側のサンプルで用意されているUINavComponentDocs(UINavigationコンテンツ>Docs)を自分のプロジェクトにコピーして編集するか、新規クラス作成で親クラスをUINavComponentとして完全にイチから作ります。新規作成の場合は最低でもUMGのデザイナーでUINavButtonを配置し名称をNavButtonと変更しておく必要があります。

今回は標準で用意されているInventorySlotというUINavComponentを使います。

UINavComponentを追加

実際にUINavWidgetのグリッドに作成したUINavComponentを追加します。

ウィジェットセットアップ完了時に発生するUINavWidgetのOnSetupCompletedイベントにDelayを1秒挟んで、動的なタイミングでの処理であるかのように書いていきます。

まず、先ほど作ったUINavComponentのウィジェットを生成しグリッドにAddChildします。

f:id:nca03132:20200428170454p:plain

これで実行すると、1秒たった後にグリッドにInventorySlotが追加されて表示されます。が、NavigationGridに何も入ってないのでゲームパッドやキーボードによるカーソル移動はできません。

f:id:nca03132:20200428170726p:plain

静的なウィジェットでは最初からボタンが配置された状態でAppendNavigationGridが呼ばれたので、NavigationGridが生成されると同時にボタンもその中に追加されていましたが、今回は先にNavigationGridを空で生成してしまっているので、後生成であるUINavComponentはAddUINavComponentノードを使って明示的にNavigationGridに追加してあげなくてはなりません。

では実際にAddUINavComponentを追記していきます。

InventorySlotが1つだとカーソルが動いているかわからないので、今回は2つ追加します。

f:id:nca03132:20200428173515p:plain

細かくて見づらいと思うので必要に応じて拡大してください。

見てわかる通りAddUINavComponentのノードが最後に追加されています。

引数には生成したUINavComponent以外に、TargetGridIndexとIndexInGridの2つがあります。

TargetGridIndexは何番目のNavigationGridに追加するかです。今回NavigationGridは1つか作ってないので0が入ります。

IndexInGridはNavigationGrid内の何番目)登録するかを指定します。これはカーソルの移動順に関係します。

通常は-1を指定し自動的に最後のインデクスを割り振らせるようにします(階層上の上から順に0,1,2,3..とインデクスが振られます)。任意の数値を入れることも可能ですが保持している最大インデクスを超えた数値を入れると-1を入れた時と同じ挙動となります。

また、すでに存在するインデクスを指定するとNavigationGrid内に同一IndexInGridを持つUINavComponentが複数存在する状態となります。この場合はStackのように後から追加したUINavComponentがカーソル移動順位的に前にくるためカーソル移動が見た目の順番と異なってしまい使う側が混乱するのでお勧めしません。

通常はUINavComponentはそれぞれ必ず異なるGridInIndexが設定されるよう管理することをお勧めします。ソートなどでアイテムの見た目の順番を入れ替えた場合もAddUINavComponentでIndexInGridを振りなおすべきでしょう。

異なるNavigationGrid間でのUINavComponentの入れ替え

鞄から別の鞄へアイテムを移動させる場合など、あるグリッドから他のグリッドへUINavComponentを移動させたいことがあるかと思います。

その場合はMoveUINavElementToGridを使います。

引数のIndexは、移動させたいUINavButtonのButtonIndex(NavigationGridローカルではなくUINavWidget全体で振られるButtonIndex)を入れ、TargetGridIndexは移動先のNavigationGridのインデクス、IndexInGridは移動先のNavigationGridのどこ(何番目)にいれるかを指定します。

この処理はは操作に関係するNavigationGridで移動するだけなので、見た目でもきちんと移動先のパネル(VerticalBox、HorizontalBox)などにAddChildしなおしてください。

これらの実装は本家チュートリアル

https://youtu.be/3f-XDgGNsPc?list=PLAcWSem_HT4hT-3viMU2_z4WW94gKhgn0&t=559

あたりで紹介されているので動画で確認したい方はリンクからどうぞ。

不要なUINavComponentの削除

不要になったUINavComponentはDeleteUINavElementFromGridを使ってNavigationGridから削除します。

引数のGridIndexにNavigationGridのインデクス、IndexInGridはGrid内インデクスを入れて削除するUINavComponentを指定します。

上記は入れ替えと同様にNavigationGrid上で消すだけなので、見た目でもパネルからRemoveChild,RemoveChildAt,ClearChildrenなどを使って削除してあげる必要があります。

こちらも本家チュートリアルでは

https://youtu.be/3f-XDgGNsPc?list=PLAcWSem_HT4hT-3viMU2_z4WW94gKhgn0&t=720

あたりで紹介されています。

 

クラスドキュメント

コンテンツブラウザ上のUINavigationコンテンツ>Docsの中には、アセット名の最後にDocsがついているサンプルが入ってます。

f:id:nca03132:20200427174826p:plain

これらは各クラスごとに、どういったノード・イベントが用意されているかを説明するためのアセットとなっています。実装時の参考にするとよいでしょう。

実装サンプル

コンテンツブラウザ上のUINavigationコンテンツ>Examplesにはさまざまサンプル実装が入っています。

アイテムメニュー、グラフィック設定、セーブロード、ポーズメニューなどよく使われるものは大体そろっていると思います。

特にUINavExampleMenuは単体でウィジェットとしてAddViewportしてそのまま使用することができ、ここを起点としてさまざまUIへ遷移する仕組みをもっているので非常に参考になります。

記事を読んである程度UINavigationについて理解しておけば、すんなりとサンプルも読めるようになっていると思うので、ぜひチェックしてみてください。

 最後に

今回の記事は下調べから動作理解、実装確認までめちゃめちゃ手間がかかりました。本当に泣きそうでした。途中何度心が折れそうになったことか。

しかもそんなにメジャーでないアセットなので何人がこれを読んでくれるかを考えると……。

いやいや、これもUE4ユーザーの礎になれば本望というものです(泣)。

というわけで、もし気になったらぜひ使ってみてください。分からないことなどあれば何なり質問してください。わかる範囲ですがお答えしたいと思います!

AdvancedLocomotionSystemV4をHumanoidRigに適用

f:id:nca03132:20200416102433p:plain


AdvancedLocomotionSystemとは

AdvancedLocomotionSystem(以下ALSV4と略)はBPベースのリアルなUE4移動制御システムです。もともと有料で売られていたのですが現在はなんと永久無料アセットとなっています。

基本移動、スライド移動、ジャンプ、ダッシュといった基本動作からFootIKによる接地処理、段差のよじ登り、銃や弓を構えながらの移動・エイムといった多彩な動作を行うことができます。

まだ詳細な動作解析はしてないのですが、とりあえず手持ちアセットで使えるようにしてみたい!ということで、セットアップ記事を書いてみました。

はじめに

本記事ではUE4標準のマネキンをALSV4で扱えるように一からセットアップしていきますが、他のスケルタルメッシュであってもHumanoidRig互換であれば同じ手順でALSV4で動くようになると思います(ただし後述しますがIK系のボーンはUE4マネキンと同じものがあったようがよい)。

ALSV4で使われているスケルタルメッシュはAnimManと名前がついているので、便宜上本記事ではALSV4のスケルタルメッシュをAnimMan、UE4のマネキンをMannequinと呼ぶ形で記事を進めていきます。

下準備

ALSV4プロジェクトを作成

まずはALSV4を新規プロジェクトとして作成します。

EpicLauncherのライブラリでALSV4を探して「プロジェクトを作成する」をクリックします。

f:id:nca03132:20200415170743p:plain

今回はALSV4という名称でプロジェクトを作ります。

f:id:nca03132:20200415171106p:plain

MannequinをALSV4プロジェクトに追加

次にALSV4を適用するためのMannequinをプロジェクトに追加します。

(※ ALSV4を適用したい自前のスケルタルメッシュがある場合、この工程を行う必要はありません。その代わりそのスケルタルメッシュをALSV4プロジェクトに持ってきておいてください。またその場合は以降の記事でMannequinに関する記述の部分は持ってきたスケルタルメッシュに置き換えて読んでください)

コンテンツブラウザの緑色の「新規作成」ボタンを押し「機能またはコンテンツパックを追加」を選択。

f:id:nca03132:20200415172105p:plain

サードパーソンを選んで緑色の「+プロジェクトに追加」ボタンを押します。

f:id:nca03132:20200415172011p:plain

これでコンテンツブラウザはGeometry、Mannequin、ThirdPerson、ThirdPersonBPが追加され

f:id:nca03132:20200415172336p:plain

という感じになります。

が、Mannequin以外のGeometry、ThirdPerson、ThirdPersonBPフォルダは不要なので削除しておきましょう。結果Mannequinだけが残って

f:id:nca03132:20200415173142p:plain

こんな感じになるはずです。

ケルトンの構造を合わせる

次が本記事のキモ、スケルトンの構造合わせです。

ALSV4は荷物を持ったり銃を構えたりする動きを実現するため仮想ボーンを必要とします。

f:id:nca03132:20200415174527p:plain

この赤い四角で囲ってある部分が仮想ボーンですね。

これ以外にもいくつかあるので、それらを追加していく必要があるのですが、ここで一つ問題があります。

仮想ボーンを追加するためには元のスケルトンにik_foot_root,ik_foot_l,ik_foot_r,ik_hand_root,ik_hand_gun,ik_hand_l,ik_hand_rというIKボーンが必要となるのです。

Mannequinのスケルトンにはそれらがそろっているのですが、そうでない場合は仮想ボーンの追加ができないため一部機能制限を受けることになります。

IKボーンがない場合は次の「仮想ボーンの追加」はスキップしていただき、「仮想ボーンが追加できない場合」に読み進んでください。

仮想ボーンの追加

先ほどのスクリーンショットではAnimManのスケルトンのHand_LにVB LHS_ik_hand_gun、さらにその子供にVB LHS_ik_hand_rが追加されています。まずはこれを追加してみましょう。

MannequinのスケルトンはコンテンツブラウザのMannequin>Character>Meshの中にあるUE4_Mannequin_Skeletonです。これをダブルクリックで開きます。

f:id:nca03132:20200415175743p:plain

まずhand_lを右クリックし表示されたメニューから「仮想ボーンを追加」を選択します。

f:id:nca03132:20200415180206p:plain

ここでターゲットボーンを選択というウィンドウが出てきました。

ボーンはあるボーンから他のボーンへの接続を表しており、仮想ボーンも例外ではありませのでhand_lからどこへつなぐのか聞かれます。

いったん作業を中断してAnimManのスケルトンでHand_Lに設定されていた仮想ボーンを確認してみましょう。

AnimManのスケルトンはコンテンツブラウザでAdvancedLocomotion4>CharacterAssets>MannequinSkeletonの中のALS_Mannequin_Skeletonとなってます。

これをダブルクリックすると

f:id:nca03132:20200415180655p:plain

追加すべき仮想ボーンはVB LHS_ik_hand_gunとなっています。

頭のVBというのは仮想ボーンでは勝手に追加されるため、このボーンの名称は「LHS_ik_hand_gun」ということになります。

名前を見てピンと来たでしょうか?

そうです、この仮想ボーンはhand_lからik_hand_gunにつなげるものです。

正確には仮想ボーンは好きな名前を付けられるため、名前から接続先が判明するわけではないのですが、AnimManの仮想ボーン名はなるべく分かりやすいようにつなぎ先を書いてくれています。

ですのでそれを参考にしたり、実際にAnimManの仮想ボーンがどことどこをつないでいるか目視で確認しながら、Mannequinのスケルトンで同じように対応するボーンにつないでいきます。

では作業に戻って実際にMannequinで仮想ボーンを繋げてみましょう。

先ほどの手順に戻ってMannequinのスケルトンを開きhand_l>仮想ボーンを追加を選んでターゲットボーン選択のウィンドウが開いたところでik_hand_と打ち込んでみます。

候補が絞り込まれるので、ik_hand_gunを選択します。

f:id:nca03132:20200415181709p:plain

これでhand_lからik_hand_gunを接続する仮想ボーンが作られました。

f:id:nca03132:20200415182053p:plain

仮想ボーンの名称はAnimManと合わせなくてはいけないので追加した仮想ボーンを選択した状態でF2を押し、AnimManと同じ「LHS_ik_hand_gun」に名前を変えておきます。

f:id:nca03132:20200415182220p:plain

これで最初の仮想ボーンの追加は完了です。いま追加したLHS_ik_hand_gunを選択した状態でプレビュー画面を見てください。

f:id:nca03132:20200415182546p:plain

左手から右手のハンドガン用IKに向けて黄色いボーンが伸びていれば正しく設定できています。

念のためAnimManの方も見てみましょう。

f:id:nca03132:20200415193805p:plain

はい、ちゃんと同じ位置に仮想ボーンがありますね。オッケーそうです。

さて、AnimManのスケルトンでは、この仮想ボーンの子にさらに仮想ボーンがついてました。

f:id:nca03132:20200415183412p:plain

LHS_ik_hand_rとなってます。この仮想ボーンはLHS_ik_hand_gunからik_hand_rにつながっています。

Mannequinのスケルトンで先ほどと同じ手順でLHS_ik_hand_gunを右クリックし仮想ボーンを追加>ターゲットボーンを選択でik_hand_rを選択します。

f:id:nca03132:20200415183315p:plain

これでLHS_ik_hand_gunの子供としてik_hand_rへつなぐ仮想ボーンが追加されます。

f:id:nca03132:20200415184212p:plain

AniimManと名前を合わせてLHS_ik_hand_rとしておきます。

f:id:nca03132:20200415184623p:plain

はい、これで大体の流れはつかめたでしょうか?

あとは上記の手順を繰り返してAnimManに設定されている仮想ボーンをMannequinで対応する箇所にどんどん追加してください。

以下、追加が必要な仮想ボーン一覧を書き出しておきます。

接続元 接続先 仮想ボーン名
hand_l ik_hand_gun LHS_ik_hand_gun
LHS_ik_hand_gun ik_hand_r LHS_ik_hand_r
hand_r ik_hand_gun RHS_ik_hand_gun
RHS_ik_hand_gun ik_hand_l RHS_ik_hand_l
ik_foot_l foot_l ik_foot_l_Offset
ik_foot_l calf_l ik_knee_target_l
ik_foot_r

foot_r

ik_foot_r_Offset
ik_foot_r calf_r ik_knee_target_r
ik_foot_root foot_l foot_target_l
ik_foot_root foot_r foot_target_r
root root Curves

(※1)接続元、接続先ともに通常のボーンとIK用のボーンを間違えないように

(※2)上記はMannequinのボーン名なので、他のデータではこの通りではない可能性があります。AnimManのボーン構成と適用したいスケルトンのボーン構成を比べながら対応する箇所に適切に仮想ボーンを作成していきます。HumanoidRig互換であれば欠損していない限り対応するボーンは見つかると思います。

設定が終わったらソケットの追加に進んでください。

仮想ボーンが追加できない場合

仮想ボーンの設定が終わっている方は、この項目はスキップしてください。

まずIKボーン・仮想ボーンがなかったらどうなるかですが、後述するアニメーションBPのコンパイル時に対応するボーンが足ないためワーニングが取り切れません。 

そのためIK系の機能が動かなくなるのでオーバーレイメニューで表示される(Qを押すと表示されるやつ)、男性っぽく歩く、女性っぽくあるく、銃を持つ、箱を持つ、タルを持つといったアクションができなくなります。

一方、可能な限りアニメーションBPのワーニングを取れば、通常移動、しゃがみ移動、ダッシュ、段差登り、ジャンプ、ローリングなどは問題なくできるようになります。

これら機能制限があることを理解したうえでお使いいただければと思います。

ということで、次のソケットの追加に進んでください。

ソケットの追加

ALSV4は仮想ボーンのほかに3つほどソケットを参照します。

AnimManのスケルトンを開くとheadボーンの下に3つほどソケットがあります。

f:id:nca03132:20200415194250p:plain

これもMannequin側にコピーしましょう。

AnimManのスケルトンでheadのボーン下にあるTP_CameraTrace_Lを選択しCtrl+Cでコピーします。

次にMannequinのスケルトンを開いて、headのボーンを選択しCtrl+Vでソケットをペーストします。

f:id:nca03132:20200415194501p:plain

これでペーストされました。

同様にTP_CameraTrace_R、FP_CameraもMannequin側にコピペしてください。

f:id:nca03132:20200415195644p:plain

MannequinにHumanoidRigを設定

MannequinはデフォルトではHumanoidRigが適用されてないのでついでに済ませましょう。(自前のスケルタルメッシュですでにHumanoidRig適用済みの場合はこの作業はスキップです)

Mannequinのスケルトンを開いてツールバーのリターゲットマネージャを選択します。

f:id:nca03132:20200415200942p:plain

次にリグを選択の部分をクリックし「ヒューマノイドリグを選択」をクリックします。

f:id:nca03132:20200415201108p:plain

終わったら保存をクリックしてスケルトンを保存しておきます。

f:id:nca03132:20200415201158p:plain

 リターゲットオプションの設定

アニメーションのリターゲットをする前にMannequinのスケルトンのリターゲットオプションを設定しておきます。

この項目は使用するスケルタルメッシュによってベストな設定が変わってくるのでケースバイケースで設定を変更してください(どういったリターゲットオプションが最適なのかは別途リターゲット関連のドキュメントを参照)。

Mannequinのスケルタルメッシュを開き、スケルトンツリータブのOptionsをクリックし、リターゲットオプションを表示にチェックを入れます。

f:id:nca03132:20200415210250p:plain

右側にトランスレーションのリターゲッティングという列が表示されるので

  • rootはアニメーション
  • pelvisはAnimation Scaled
  • ik系統(ik_で始まるボーン)はアニメーション
  • 上記以外はSkeleton

という設定にしておいてください(標準状態だとpelvisの設定を変えるだけかと思います)。変更したらスケルトンを保存しておきます。

アニメーションBPをリターゲットする

ケルトンの構造を合わせ終わったのでいよいよアニメーションBPのリターゲットを行います。

アニメーションBPのリターゲット処理

コンテンツブラウザでAdvancedLocomotionV4>CharacterAssets>MannequinSkeletonを開いて、中にあるALS_AnimBPを右クリック。

Animブループリントをリターゲットする>Animブループリントとリターゲットを複製を選択します。

f:id:nca03132:20200415201503p:plain

ケルトン選択ウィンドウが表示されるので

  • UE4_Mannequin_Skeletonを選択
  • 新規アセット名のSuffixに_Mannequinを指定
  • フォルダの指定でMannequin/Animationsを指定

上記設定を行った後、リターゲットボタンを押します。

f:id:nca03132:20200415202034p:plain

これでアニメーションBPが生成されたはずです。

アニメーションBPのエラー・ワーニング確認

次にアニメーションBPをコンパイルしてエラーやワーニングが出ないか確認します。

AnimManのスケルトンのボーン名と持ち込んだスケルタルメッシュのボーン名が違う場合、アニメーションBPのコンパイル時にワーニングが出ます。

 f:id:nca03132:20200416145544p:plain

これらが出ている箇所についてはALSV4のIK機能は動いていません。

従って、上記ワーニングが出ている箇所について、持ち込んだスケルタルメッシュ上で対応するボーン名に書き換える必要があります。

ワーニングが出ているボーンが持ち込んだスケルタルメッシュのどのボーンに該当するかは、リターゲットマネージャのリグの設定を参考にするとよいでしょう。

f:id:nca03132:20200416152647p:plain

キャラクターBPの作成

次にMannequin用に新しくキャラクターBPを作成します。

といってもAnimManのキャラクターBPをコピーするだけなので簡単です。

コンテンツブラウザでAdvancedLocomotionV4>Blueprints>CharacterLogicの中にあるALS_AnimMan?CharacterBPをMannequin>Characterにドラッグ&ドロップし、「ここにコピー」を選択します。

f:id:nca03132:20200415203912p:plain

コピーしたアセットはBP_Mannequinにリネームしておきます。

f:id:nca03132:20200415204154p:plain

次にこのBP_Mannequinを開いて、コンポーネントでMeshを選択し、SkeletalMeshにSK_Mannequin、AnimClassにALS_AnimBP_Mannequinを選択します。

f:id:nca03132:20200415204346p:plain

次はレベル上に作成したキャラクターBPを配置して動作確認をします。

動作確認その1

作成したBP_Mannequinをレベルに配置します。

f:id:nca03132:20200415204910p:plain

最初からレベルに配置されていたALS_AnimMan_CharacterBPはAuto Possess Playerが設定されているのでDisableにします。

f:id:nca03132:20200415205420p:plain

そして配置したBP_MannequinのAuto Possess PlayerをPlayer0に設定します。

f:id:nca03132:20200415205534p:plain

あとはプレイを押せばMannequinがプレイヤーとして動くようになるはずです。

前後左右やダッシュ、ジャンプといった基本的な移動はできるので確認してみてください。

段差のよじ登り、ローリング、ラグドールからの復帰といったアクションを伴うものはこの段階ではまだできません。IKによる足の設置なども対応されていません。

また終了時に大量にエラーが出る状態となっています。

アクション用のアニメーションをリターゲットする

次にアクション関係の対応です。

アクション用アニメーションのリターゲット

コンテンツブラウザでAdvancedLocomotion4>CharacterAssets>MannequinSkeleton>AnimationExamples>Actionsを開いて、Ctrl+Aを押して中のアニメーションアセットすべてを選択状態にし、右クリックでAnimアセットをリターゲットする>Animアセットとリターゲットを複製するを選びます。

f:id:nca03132:20200415214942p:plain

ケルトン選択ウィンドウが開くのでアニメーションBPをリターゲットしたときと同じように

  • UE4_Mannequin_Skeletonを選択
  • 新規アセット名のSuffixに_Mannequinを指定
  • フォルダの指定でMannequin/Animationsを指定

上記設定を行った後、リターゲットボタンを押します。

f:id:nca03132:20200415215149p:plain

これでアクション用にアニメーションアセットが作成されました。

BP_Mannequinへの適用

作成したアクション用のアニメーションアセットをBP_Mannequinで使用するよう設定します。BP_Mannequinを開いて変数の中の

Mantle Animations,RollAnimations,GetUpAnimations

の項目の中にあるすべてのAnimMontage指定を同名でSuffixに_Mannequinがついているのもに置き換えていきます。

f:id:nca03132:20200415215616p:plain

すべて置き換えたらコンパイル&保存します。

終了時エラーの対処

BP_Mannequinを変更しているので、終了時のエラー対応もしてしまいましょう。

ALSV4はデバッグ機能でメッシュのカラー変更を行えるようになっているのですが、自前で用意したスケルタルメッシュはこれには対応してないので、その部分を削除します。

BP_MannequinのイベントグラフでEventTickの中で呼ばれているUpdate Coloring Systemの呼び出しを削除し、Update Held Object Animationsへ実行ピンをバイパスさせます。

f:id:nca03132:20200415221755p:plain

またその下にあるメッシュをトグルするシステムも対応してないので丸っと削除します。

f:id:nca03132:20200415222100p:plain

コンストラクションスクリプトを選択し、ダイナミックマテリアルを設定している箇所も非対応なので削除します。

f:id:nca03132:20200415222343p:plain

これで終了時エラーは出なくなるはずです。

使わなくなった関数のColoringSystemのネームグループと変数のColorsもすべて削除しておきます。

f:id:nca03132:20200415222757p:plain

動作確認その2

この段階で一度動作検証をしておきましょう。

段差よじ登りやローリングなどのアクションが可能になっているはずです。足のIKによる接地処理も動作しているはずです。

唯一ラグドールからの復帰がきちんと動かないので、次で対応を行います。

物理アセットの設定を合わせる

最後に物理アセットをAnimManに合わせて調整します。

  • AnimManの物理アセット AnimMan_PhysicsAsset
    AdvancedLocomotionV4>CharacterAssets>MannequinSkeleton>PhysicsAssets>AnimMan_PhysicsAsset
  • Mannequinの物理アセット SK_Mannequin_PhysicsAsset
    Mannequin>Character>Mesh>SK_Mannequin_PhysicsAsset

の2つを比較してみます。

f:id:nca03132:20200415224732p:plain

AnimManの物理アセットにはrootのシェイプが作成されていることがわかります。(赤線で囲んだ場所)。

どういったものなのか確認するためにrootのシェイプを選択した状態で画面右側の目玉アイコンから「変更したプロパティのみを表示」にチェックを入れてみます。

f:id:nca03132:20200415225439p:plain

これで標準状態から変更を行ったプロパティのみが表示されます。

f:id:nca03132:20200415225718p:plain

見てみるとPhysicsの設定でAngular Dampingに50が設定されており、PhysicsTypeにKinematicが指定れてます。さらに、CollisionReponseのチェックが外れており、ボディセットアップで半径7.505のSphereがContribute to Massにチェックが入った状態になってます。

とりあえずこれと同じものをMannequin側にも追加する必要があります。

SK_Mannequin_PhysicsAssetを開いて、スケルトンツリーのrootを右クリックしAddShape>AddSphereを選択して球形コリジョンを追加します。

f:id:nca03132:20200415230213p:plain

追加されたシェイプを選択状態にして、右側の詳細パネルの設定をAnimManの設定に合わせて変更します。

f:id:nca03132:20200415230527p:plain

具体的には

  • Angular Damping 50
  • PhysicsType Kinematic
    f:id:nca03132:20200415231003p:plain
  • CollisionResponse チェックを外す

    f:id:nca03132:20200415231051p:plain

  • ボディセットアップのPrimitives>Spheres>0で
    半径 7.505
    Contribute to Mass チェックを入れる

    f:id:nca03132:20200415231403p:plain

といった設定を行います。

あとは、同様の方法でAnimMan_PhysicsAssetの各項目を上から1つずつ確認していき、デフォルトから変更されたプロパティがあれば、Mannequin側のパラメータも合わせて変えていく対応を行っていきます。

ざっと確認したところ、すべてのShape(丸に緑の小さい丸が3個入ってるようなアイコン)のAngular Dampingが50になっているようなので、そこも対応したほうがよさそうですが、スケルタルメッシュのデータ次第なところもあるので適時Mannequin側には調整を行っていただければと思います。

動作確認その3

これでほぼすべての実装は動くようになったかと思いますので、最終確認してみてください。

トラブルシューティング

銃の位置補正

ALSV4では右手に持つ銃は仮想ボーンのRHS_ik_hand_gunにアタッチされるようになっていますが、スケルタルメッシュによっては銃がちゃんとした位置にならないことがあるようです。

はるべえさんの動画(https://www.youtube.com/watch?v=fsP68yQHIbs)では、この回避策として別途スケルトンに銃用のソケットを用意して銃のアタッチはそちらに行うようキャラクターBPを書き換える方法を採用していました。(動画の10:00~あたり)

膝が外向きでおかしな状態になる

仮想ボーンでボーン構造の整合性を取ろうとしたときにIKがおかしくなることがあるようです。原因調査中ですが、いったんは膝関連のIKを外せば動くようになると思います。

アニメーションBPのAnimGraphでFootIKの部分をダブルクリックで開き

f:id:nca03132:20200416170750p:plain

Modify Knee Targets,Apply IK to Feetの部分をバイパスしてつなぎます。

f:id:nca03132:20200416170946p:plain

最後に

もうちょっと完結にまとめられるかと思ったんですが、もろもろ書いてるうちに結構な量になっていまいました。しかも後半かなり駆け足…。

ALSV4は素晴らしいアセットで自作ゲームに取り込みたい方もたくさんいるかと思います。この記事が少しでもそういった方々の助けになってくれたらなと思っています。