UE4の勉強記録

UE4の勉強の記録です。個人用です。

「Unreal Engine4.xを使用してRPGを作成する」の6章、NPCらと対話(NPCs and Dialog)を勉強する。

<前文>

f:id:kazuhironagai77:20190714135027p:plain

最近るろうに剣心を読み直す機会がありまして、こんなに面白かったのかとビックリしました。特に新選組が出てくると面白さが格段に違います。そういえば新選組はどの世代でも人気がありますね。その理由をちょっと考えてみたのですが、敗北すると分かっているのに忠義を重んじて戦いに出る新選組の隊員たちが単純にカッコイイからだと思います。老若男女、社会で常に長い物には巻かれろを強いられる日本社会で、敢えて敗北すると分かっているのに忠義を通す新選組の隊員たちは全ての世代の日本人にとって等身大のヒーローなのかもしれません。

それでは今週も勉強を始めます。

<本文>

<目的>

「Unreal Engine4.xを使用してRPGを作成する」の6章、NPCらと対話(NPCs and Dialog)を勉強します。大分進みましたね。3章を勉強していた時は何時終わるのか見当もつかない状態でしたが、今年中には終わりそうですね。

この章ではNPCを作成します。NPCは情報のハブと店のオーナーの役割を果たします。この章では以下の事を行います。

  • NPCキャラクターブループリントの作成
  • NPCとの対話
  • ダイアログボックスの準備
  • NPCウェルカムボックスの作成
  • NPC会話ボックスの追加

<方法と結果>

<Step.0>

勿論、Ch2を使用してやって行きます。

まず、サンプルコードを解凍します。

f:id:kazuhironagai77:20190825201726p:plain

特に問題なく出来ました。

f:id:kazuhironagai77:20190825201745p:plain

UE4エディターから開いても問題ありません。

<Step.1>

NPCキャラクターBPの作成(Creating the NPC Character Blueprint)

新しいBPクラスをキャラクタークラスを親にして作成します。名前はNPC_ShopOwnerです。

f:id:kazuhironagai77:20190825201834p:plain

まず可視出来るNPCを作成します。

f:id:kazuhironagai77:20190825201853p:plain

出来ました。具体的な作業は教科書に書かれている通りにやりました。特に目新しい事は書かれていませんので具体的な作業の記録は省略します。

<Step.2>

今度は、キャラクターと対話するための方法を作成します。

Add Component->CollisionからBoxCollisionを選択してCapsuleComponentに追加します。

f:id:kazuhironagai77:20190825201938p:plain

作成しました。サイズと位置は教科書の説明通りに変更しました。

また、教科書ではCollision PresetsをOverlapAllDynamicにセットするように書かれていますが、4.22では最初からOverlapAllDynamicにセットされていましたのでそのままにしています。

f:id:kazuhironagai77:20190825201956p:plain

<Step.3>

NPCをレベル内にドラック&ドロップします。

はい。ただ教科書の写真を見ると、レベルが明らかにデファルトのものではありません。そこだけサンプルで確認します。

分かりませんでした。

f:id:kazuhironagai77:20190825202036p:plain

分からないので上記のようにデファルトのレベルに配置しました。

<Step.4>

NPCとの対話(Interacting with the NPC)

トリガーボリュームを使用したNPCとの会話を作成します。

とありました。教科書のその後の解説が結構、大切に思えたので以下に簡単にまとめました。

まず、ロジックを考えます。

  • プレイヤーがNPCと対話できるのは、プレイヤーがNPCの視野に入った場合(プレイヤーがNPCのトリガーボックスに入った場合)のみです。
  • もしプレイヤーがNPCのトリガーボックスに入った場合、Yesを返すBooleanが必要です。
  • 更に、そのBooleanYesを返した時だけ、プレイヤーがキーを押す事でNPCと対話させたいです。
  • このBooleanは幾つかのクラスから使用される可能性がありますので、RPGGameInstanceクラスでグローバル変数として宣言します。
  • このBoolean Game Dataのカテゴリー内に分類されます。ただし他のGame Data内の変数と違いブループリント内から変数の値が読めるだけでなく書き込めます。

うん。まとめてしまえばそんなに大切でもなかったかもしれませんね。

ここで、私が大切と思ったのはGameInstanceクラスにこのBoolean変数を作成しなければならない論理性です。正直、このBoolean 変数をBP上で作成した場合との違いがまだ分かりません。他のC++で宣言した変数はBPから見る事が出来ても書き込む事は出来ません。

まず、今までの復習ですが、私の考えではBPの作成もプログラマーが担当します。ただし、その後で、デザイナーがもっと良いデザインをするためにプログラマーが作成したBPを改良する事はOKです。のでプログラマーは、デザイナーに改良してもらいたい部分をBPで作成し、デザイナーが担当すべきでない箇所はC++で直接書きます。

今回のBoolean変数は、どっち何でしょうかね。これは後で考察で考える事にします。

では、やって行きます。

f:id:kazuhironagai77:20190825202203p:plain

教科書に書かれている通りに、RPGGameInstance.hにBoolean変数(TalkShop)を追加しました。

f:id:kazuhironagai77:20190825202229p:plain

更に、教科書の方法でTalkShop変数を初期化しました。

<Step.5>

Player/keyのインターアクション(interaction)とTalkShopがTrueかFalseの確認を行います。

  • プレイヤーコントローラーは直接NPC_ShopOwnerにアクセス出来ないので、キーインターアクションはFieldPlayerクラス内かレベルブループリントクラス内で作成する必要があります。
  • NPCはレベルに依存している場合がほとんどなので、キーの作成とbooleanのチェックはレベルブループリント内で行います。

とありました。納得です。

納得ですが、私のプロジェクトでは、今はデファルトのレベルを使用しています。これをそのまま使用していいのかだけサンプルコードで確認します。

サンプルコードのMinimal_Default.umapのレベルブループリントを開きます。

f:id:kazuhironagai77:20190825202259p:plain

f:id:kazuhironagai77:20190825202307p:plain

あれ、何も書かれていません。やっぱり他のレベルブループリントを使用しているのでしょうか?

f:id:kazuhironagai77:20190825202324p:plain

サンプルコード内にある全てのumap拡張子を調べて見ましたがありません。この部分のサンプルは存在しないみたいですね。

なのでデファルトのレベル内に作成します。

f:id:kazuhironagai77:20190825202400p:plain

教科書通りに作成しました。

この教科書はPC以外からの操作を考慮していないのでKey Eventをそのまま使用していますが、アンドロイド用のRPGにするためにはこの辺の改良が必要ですね。

更に続きを作成しました。

f:id:kazuhironagai77:20190825202455p:plain

教科書ではここでコードが正常に作用しているのかテストしています。 流石にこの程度の内容なら間違える事はないと思いますが確認は大切です。

一応テストしてみます。

f:id:kazuhironagai77:20190825202517p:plain

プレイヤーが離れている所では、Eを押しても何も反応しません。

f:id:kazuhironagai77:20190825202535p:plain

プレイヤーがNPC_ShopOwnerの前に立った状態でEを押すとHelloが表示されました。

出来ていますね。

<Step.6>

  • もし、プレイヤーがNPC_ShopOwnerの前でEを押し続けたら、ずっとHelloが表示されます。
  • これを直します。

Eを押してNPCが返事をするのは一回でいいと言う事でしょうか?ちょっと意味が分からないですが続きをやって行きます。

f:id:kazuhironagai77:20190825202605p:plain

教科書の指示通りにNPC_ShopOwnerのBP内に作成しました。

分かりました。今までのBPだとプレイヤーが一回NPC_ShopOwnerの前に立ったらその後はプレイヤーが何処にいてもEを押すとHelloが表示されます。上記のコードを足した場合はプレイヤーがNPC_ShopOwnerの前に立った時だけ、Eを押す事でHelloが表示されます。

試したらその通りに成りました。

<Step.7>

ダイアログボックスの準備(Dialog box setup)

  • ダイアログの作成をするために、まずNPCの全ての親の変数のハウジング(housing) ―例えばゲームのダイアログ―のためのウィジェットブループリントを作成します。
  • その関数内のダイアログ変数を呼ぶ事でいつでも必要な時にダイアログを引っ張って来る事が出来ます。
  • この処理はUMGに直接テキストするよりベターです。なぜならこの方法なら、ダイナミックにテキストが入るシングルのダイアログのUMGだけで済むからです。

とありました。

ダイアログの作成の仕方は全く知らないのでここでしっかり勉強したいです。

f:id:kazuhironagai77:20190825202646p:plain

まず、全てのNPCの親になるUMGウィジェットNPC_Parentを作成します。

f:id:kazuhironagai77:20190825202707p:plain

変数NPCDialogを追加します。タイプはTextで配列にします。更に他のクラスからもアクセス出来る様にpublicにします。

f:id:kazuhironagai77:20190825202727p:plain

変数のデファルト値を2つほど作成します。

<Step.8>

ウィジェットBP、DialogBoxの作成

f:id:kazuhironagai77:20190825202800p:plain

まず、UIの部分のデザインだけ終わらしました。

次にExitの機能をBP内に書き込みます。

f:id:kazuhironagai77:20190825202841p:plain

<Step.9>

NPCウェルカムボックスの作成(Creating an NPC welcome box)

  • ダイアログボックスのテンプレートが出来ました。
  • それを使用して今作成したNPCのためのカスタム化したダイアログボックスを作成します。

DialogBox をコピーしてShop_Welcomeを作成します。

f:id:kazuhironagai77:20190825202923p:plain

<Step.10>

適切な時に、このスクリーンを表示するために、レベルブループリントのコードを改良します。

はい。

以下に示すように改良しました。

f:id:kazuhironagai77:20190825203202p:plain

テストします。

f:id:kazuhironagai77:20190825203226p:plain

出来ました。

<Step.11>

NPC会話ボックスの追加(Adding an NPC talk box)

プレイヤーがtalkボタンを押した時に開くダイアログボックスを作成します。

教科書に書かれている通りにDialogBox をコピーしてShop_Talkを作成します。

f:id:kazuhironagai77:20190825203309p:plain

Shop_Welcomeウィジェットブループリントのtalkボタンをクリックした時にShop_Talkが開くようにします。

f:id:kazuhironagai77:20190825203327p:plain

テストします。

f:id:kazuhironagai77:20190825203346p:plain

出来ました。

<考察>

今回も特に難しい部分はありませんでした。ダイアログボックスの作成は初めてでしたのでちょっと興味があったのですが、エクセルとかに別に保存してそれを読み込むのではなく、単に親のウィジェットブループリントの変数に保存しておくだけでした。うーん。正直もう少し高度なゲームデザインを期待していました。

残り数章でこの本はお終いですが、この本の勉強だけではRPGは作れないかもしれませんね。

<Step.4>におけるBoolean変数の宣言場所についてですが、教科書に書かれている通り、「このBooleanは幾つかのクラスから使用される可能性がありますので、RPGGameInstanceクラスでグローバル変数として宣言します。」が一番大きな理由と思います。ただしデザイナーにいじってほしくない変数をブループリント上に変更可能な状態で残すのは、やっぱり良くないと思います。思いますが他の選択肢がない場合はしょうがないのかもしれません。デザイナーもデザインに関係ない箇所を積極的にいじる事もないでしょうし、完璧を目指すのは良いですが無理な場合は妥協も大切と言う事でしょうか。