nemesのブログ

初心者による初心者のための初心者を言い訳にした色々創る記録

UE4でNPCとの会話を発生させたい。

UE4NPCとの会話を発生させたい。

f:id:nemes:20180913230738j:plain

 

RPGなどで必ず必要な会話。キャラクターを操作し、NPCの近くで何らかのボタンを押すと会話を始める、こんなイベントを実装してみたい。

今回述べてる会話とは、NPCの近くで指定の操作をするとUMGが立ち上がり予め用意しておいたテキストを表示するというもの。ちなみにUMGがなんの略かは知らない。(UnrealMotionGraphicsらしい)

また、選択肢によってその後NPCに異なる動作をさせるところまで出来たら上出来かな。ゲームの中でくらい会話したいよな。

 

ということで今回の理解のゴール。

・インプット処理

コリジョンイベント処理

・UMG作成

・ブループリントとUMGの連携

・選択肢による動作

毎度ながら適当な定義のゴール。まぁやっていきましょう。

 

 

0.準備

新規プロジェクトをThirdPersonExampleMapで作成。

インプット処理を追加しておく。今回はキーボードFをアクションボタンとする。

編集タブ>左ペインインプット>BindingsでActionMappingsの+ボタンを押す。名前をTalkにしてFキーを割り当てる。

f:id:nemes:20180808200545j:plain

 

1.会話対象の作成と配置

・会話の相手を作る。コンテンツ内で右クリックブループリントクラス>アクターを作成。名前をBP_TalkingActorに変更。

・BP_TalkingActorをダブルクリック。左上コンポーネントを追加、StaticMeshを選択。StaticMeshをDefaultSceneRootに重ねる。

・右詳細パネルのStaticMesh欄のStaticMeshでSM_TableRoundを選択。なんでもいいです、壁とも会話できますが今回はテーブル。

f:id:nemes:20180808202601j:plain

コンパイルと保存をしてレベル内の適当な場所に配置。

 

2.確認用のUMG作成

 ・今回は標準で配置してあるThirdPersonCharacterに処理を書いていこう・・・と、その前にとりあえずのUMGを作成しておく。仕上げはあとでします。

・コンテンツに新しいフォルダを作成、名前はUMG。その中で右クリックし、ユーザーインターフェイスウィジェットブループリントを作成。名前をTalkBoxに変更。

・TalkBoxを開き、要素を追加していく。左上パレットでCanvas Panelと検索、画面内にドラッグ。アンカーはこれ。

f:id:nemes:20180811020452j:plain

数値はこんな感じf:id:nemes:20180811020548j:plain

・左上パレットでImageと検索、検索に出てきたImageをドラッグして今作ったTalkPanelに重ねて親子付けする。名前はTextWindow。

f:id:nemes:20180816001254j:plain

・アンカーはさっき選んだやつの一つ上の左右下揃えのやつ、その他数値はこんな感じ。Appearance>Brush>Tintで色を変更。好きな色でいいです、また、お好みでアルファで透かしましょう。とりあえずここまでやって放置。

f:id:nemes:20180811021319j:plain

 

 

 

3.ブループリントの実装

・今回の肝です。先にこんなこというのはかなりダサいですが、僕は最近UE4の勉強を始めた素人です。おそらく改良の余地があるものが出来上がりますが大目に見てね。

・イメージはこんな感じ、超超々大雑把だけど。

f:id:nemes:20180822231353j:plain

これしか考え付かんかったわガハハ。愚直にこれを実装していこうと思う。

・先ず、ThirdPersonCharacterにコリジョンを追加する。コンテンツ>ThirdPersonBP>Blueprints>ThirdPersonCharacterを開く。左上のコンポーネントを追加をクリックしCapsuleCollisionを追加。名前をActionCollisionに変更。これが検知コリジョン

・追加したActionCollisionをトランスフォームする。CapsuleComponentより少し大きいくらいに。

f:id:nemes:20180821032928j:plain

 ・ActionCollisionのCollision設定をする。ActionCollisionを選択した状態で詳細パネルを下にスクロールするとCollisionの項目があるのでここの設定をする。ここについては公式のリファレンスが分かりやすいのでそちらを読んでもらえると理解しやすいかと。

api.unrealengine.com

api.unrealengine.com

・自分が理解してないが故に公式リファレンスを貼って説明を濁すという逃げですが僕自身わからない言葉が多くて(調べろや)説明できないのでごめんなさい。なのでそれっぽく設定してとりあえず動いたら後から理解を追いつかせるスタイルでいきたいと思いま。

・ThirdPersonCharacterのActionCollisionの詳細パネルのCollision設定はこのようにします。(Simulation Generates Hit Event のチェックは消しても構いません)

f:id:nemes:20180821040023j:plain

 Generates Overlap Eventsにチェックを付けることでこのコリジョンが何かにオーバーラップされたときにイベントを発生させるようにします。Collision Enableとか結構重要な気もしますが説明を読んでもよく分かりませんでした。他については何でもかんでもとりあえずオーバーラップさせるという設定。

Simulation Generates Hit Event これはヒットイベントで使うときのやつ。

単純にコリジョン判定をオーバーラップにしたいならGenerates Overlap Eventsにチェック。コリジョン判定をヒットにしたいならSimulation Generates Hit Eventにチェックをつけるという認識でいいかと。

・そしたら実際にブループリントを書いていく。ActionCollisionを選択し、詳細パネルを下にスクロールしていくとグリーンのボタンが並んでいるイベント項目がある。ここから目当てのイベントを追加していくと楽。今回はオーバーラップを使うのでOn Component Begin OverlapとOn Component End Overlapの+部分を押してブループリントに追加する。

f:id:nemes:20180822233949j:plain

多分こんな感じになると思う。

On Component Begin Overlap=文字通りこのコリジョンコンポーネントが侵入すると発動する。

On Component End Overlap=コリジョンからコンポーネントが出ていくと発動する。

わかりやすくていいね。

・会話対象BP_TalkingActorと会話できるかの判定を入れる変数を追加する。BP_TalkingActorを開く。ビューポートタブで左下の変数のプラスボタンを押す。名前をTalkableに変更、変数の型をBooleanに変える。コンパイルすると右側の詳細パネルにデフォルト値の項目が追加されるのでチェックが外れているのを確認する。

f:id:nemes:20180823004134j:plain

このアクターが操作キャラの検知コリジョンに入ったら会話可能、つまりTalkableにチェックがつくようにする。 

・ThirdPersonCharacterのイベントグラフに戻る。ほんだらこんな風に書く。

f:id:nemes:20180823014653j:plain

 まず、Cast To ノードについて簡単に説明する。

キャストとは、キャスト元、今回はCast To BP_TalkingActorのObjectピンに繋がっているOnComponent~ノードのOther Actorピン(コリジョンが検知したActor)がBP_TalkingActorかどうかを確認するために使用する。

f:id:nemes:20180823013709j:plain

 今回の場合だと検知コリジョンにオーバーラップしたのがBP_TalkingActorであれば上のピンにパルスが流れることになる。

また、右側下のAs~ピンを使うことによりBP_TalkingActorの要素にアクセスすることが出来る。これは便利。

それでさっきの画像に戻り左から解説していくと。。。

f:id:nemes:20180823014815j:plain

 On Component Begin Overlap > Cast To BP_TalkingActor

・・・ここでコリジョンに入ってきたActorがBP_TalkingActorか判定。

Cast To BP_TalkingActor > セット(Talkable)

・・・ひとつ前の判定がイエスであれば、Talkableにアクセスしチェックを付ける(Trueにする)

・最後のノードは確認用のプリントノード。ここまでできたらコンパイルしてプレイしてみよう。

f:id:nemes:20180823020109j:plain

検知コリジョンに入ったらTalkableがTrueになり出たらFalseになるはず。

・これをアクションキーと組み合わせる。多分画像のコメント見れば理解できるはず。

f:id:nemes:20180823022407j:plain

 コリジョン範囲外でFを押す。謎の躍動感

f:id:nemes:20180823022843j:plain

コリジョン範囲内でFを押す

f:id:nemes:20180823022912j:plain

やったぜ。

・・・・

・・・

・・

( ^ω^)・・・ん?

f:id:nemes:20180825033423j:plain

なんか出とる

f:id:nemes:20180825033500j:plain

Blueprint Runtime Error: "Accessed None trying to read property K2Node_DynamicCast_AsBP_Talking_Actor". Blueprint: ThirdPersonCharacter Function: Execute Ubergraph Third Person Character Graph: EventGraph Node: ブランチ

・この解決にかなり時間を使いました。というのも動作としては全く問題なく動いていたから何がどうダメなのかプレイ画面や操作からでは分からない。だけど無視するわけにもいかない。じゃあログから読み取るしかないよね?という初心者にとっては一番たちの悪いやつ。

・仕方ないのでログでググってみると似たような事象がちょいちょい出てきた。それらによると「有り得ないものにアクセスしたよ」って感じ。ブループリント的に言うと「入力されているオブジェクトで正しくない(有効でない)ものがあるよ」ということ。じゃあ、やることは一つでそのオブジェクトを有効にしてやろう。

・ここで出てくるのが「IsValid」ノード。繋いだオブジェクトが有効かどうかを判定してくれる単純なノードです。ブループリント上で右クリックしIsValidで検索してみよう。

f:id:nemes:20180827000755j:plain

 ?ついてるほう。

f:id:nemes:20180827001517j:plain

・左下のピンに繋がれたオブジェクトが有効であれば右上の「Is Valid」にパルスが流れ、有効でなければ「Is Not Valid」にいきます。

f:id:nemes:20180827001346j:plain

こんな感じで 割り込む。これで見事にエラーは出なくなったかと。

こんな単純なことです、でもこんなことに数時間躓くのが初心者です。でも、もしまたこのエラーが出たときは瞬殺です、これを積み重ねていく。

残念ながら今回で言う「有効」というのがUE4ではどういう意味なのかは説明できません。初心者なもんでね、すまぬ。

 

4.ブループリントとUMGの連携

・操作の方はなんとかできたのでとうとうUMGと連携させる。先ずは単純に会話可能なときにFキーを押したらウィジェットが立ち上がるようにする。

・実はこの過程でまた躓き三日悩んだ挙句UE4のアンサーハブに助けを求めました。拙者も華麗にAnswerHubデブーしてしまったでござるwコポォww。はい。せっかくなんではっとく。

キー入力からウィジェットを表示したい - UE4 AnswerHub

アンサーハブの回答者様ってすごいのな、質問を寝る前に投稿して寝て起きて仕事行って帰ってきたときに解答きてればいいな~とか思ってたんだけど、寝た後深夜にトイレで一回起きたらもう解答きてた。シュゴイ。たぶん見てないだろうけど改めてお礼申し上げます。

・(ここは別に読まんでもええ)ちょっと脱線してるけどもう一つ言いたいことがあって、もし独学で躓いたときにどのくらい自分で悩むべきかってことはなんとなく決めておいた方がいい。っていうのも今回自分は三日ほど悩んで(時間で言うと6時間くらいだけど)結局、他の人に聞いたら五分で済んだわけよ。でも、この悩んでた時間に試行錯誤したり色んな事調べたり、答えは見つからずとも有益な知識を得たりもしたの。やっぱり学習において自分で考えたりする時間ってのは重要だと思う。だから、もし分からないことがあっても直に人に聞かずに少しでも時間を使って答えを探そう。まぁもちろん人に聞かないと絶対分からないこともあるんで、悩み続けて進まなくならないようにある程度で妥協する期限も設けよう。ちゅうことや、途中自分でも何言ってるかわからんくなったが伝わってくれると嬉しい。

・続けよう。ウィジェットの表示には一連のセットみたいなのがあるのでまずはそちらを紹介する。

f:id:nemes:20180901032932j:plain

TalkBoxウィジェットを作成・・・クラス項目で選択したウィジェットブループリントを呼び出し。今回はプルダウンメニューからTalkBoxを選択

セット・・・TalkBoxへセット

Add to Viewport・・・ターゲットに繋がれたノードを画面に表示する。

このセットを上手い具合にThirdPersonCharacterの会話操作に組み込む。

・考えとしてはBeginPlayからセットまでは予め行っておき、会話のタイミング、つまりLet'sTalk!!が出力されるタイミングをAdd to Viewportに置き換えればいい。するとこうなるよね。

f:id:nemes:20180901034100j:plain

 プレイして確認。やったなおい。UMGでたときめっちゃ嬉しかったわ。

f:id:nemes:20180901034405j:plain

 多分、Fキー押しまくると警告でるけどとりあえず無視でいいです。

The widget 'TalkBox_C' was already added to the screen.

・表示はできたので次は非表示。現状だとTalkBoxが出っぱなしだよね。結果から言うと下の画像。

f:id:nemes:20180901034910j:plain

・これで消えます。正確に言うと隠れます。見た目では消えたように見えるけど実際はデータを保持したまま隠れてる感じ。

こちらのブログ様がとても分かりやすくまとめられているので是非読んでみてね。

limesode.hatenablog.com

・プレイしてみよう。TalkBox表示後に検知コリジョン範囲から出ると見事に消えるかと思います。

・今回の機能は会話中は動けなくなり、会話が終わる or UMGの閉じるボタンを押したら動けるようにする。なのでコリジョンから出たらUMGが消える機能は将来的には必要なくなるかも?それと上で出た警告

The widget 'TalkBox_C' was already added to the screen.

これも自動的に消える。Fキーが一回しか押せなくなるからね。

・動けなくするのはこうするだけ。文字通りのノード。これで会話が始まると全く動けなくなる。

f:id:nemes:20180902002725j:plain

 ここまででひとまずは操作キャラの設定は放置しておきましょう。

・久々にUMGちゃんをかまってあげよう。TalkBoxのデザイナーを開く。パレットでtextと検索しTextを階層パネルの[TalkPanel]に3つ追加する。それぞれの名前をText_ActorName,Text_Talk,Text_Cntnr、にする。

f:id:nemes:20180902022031j:plain

・それぞれを設定する。ここではUMG設定の各項目の説明は行わない。気になったものは自分で調べてみるといい。自分も理解していないものが多いけど複雑なものは少ないからすぐ理解できるよ。

 まずはText_ActorName

f:id:nemes:20180902022833j:plain

赤〇が変更箇所。

アンカーはこちら。残り2つのテキストも同じアンカー。

f:id:nemes:20180902022918j:plain

 

Text_Talk

設定項目が隠れていることがあるのでその時は▼を押そう。またContentのTextはShift+Enterで改行できる。

f:id:nemes:20180902023329j:plain

f:id:nemes:20180902023343j:plain

 

Text_Cntnr

f:id:nemes:20180902023855j:plain

f:id:nemes:20180902023904j:plain

各テキストで場所とか色とかフォントサイズとか気に入らなければ勝手に変えてどうぞ。

結果確認。

f:id:nemes:20180902024247j:plain

・テキスト部分会話相手の名前になり、右下の...は次のページにいけるよっていうアレ。

 ・動作確認。見栄えはしょぼいけどいい感じ。

f:id:nemes:20180902024632j:plain

・名前をBPに関連付ける。

階層パネルでText_ActorNameを選択。詳細パネルContent>Textのバインドをクリック。バインディングを作成。

f:id:nemes:20180902223025j:plain

・すると自動的にグラフ画面に遷移する。

・変数を追加し、名前をTalkerName、変数の型をTextに変更。リターンノードにそのまま突っ込む。

f:id:nemes:20180902223917j:plain

・次はテキストの内容を変える。デザイナー画面に戻り、Text_Talkを選択。名前同様にバインドをクリック。変数を新規作成。名前をTalkerTextに変更、変数の型はString。Getしてリターンノードにぶち込む。今回は型が違うので自動でコンバートされる。

f:id:nemes:20180902224723j:plain

コンパイルする。左下のTalkerTextにテスト用のテキストを入力する。

f:id:nemes:20180902225617j:plain

・プレイじゃ。

f:id:nemes:20180902230122j:plain

 ・どんどんいこう。次はテキストを一文字ずつ表示させるようにする。グラフ画面に戻る。

・とりあえず答えから貼る

f:id:nemes:20180909220447j:plain

 このブループリントはUdemyのたすさん(

たす (@tassjp02) | Twitter

)の講座から拝借しものです。

www.udemy.com

自分で配列を使ったやり方を考えたかったんだけど、考えるに考えて今回は諦めた。自分にはちょっとまだ無理そうに感じた。時間あったら自力でできるようになりたい。 

・変数を追加する。

TalkInterval

・・・これは文字が表示されてから次の文字が表示されるまでの時間を設定する変数。

f:id:nemes:20180909222335j:plain

 

TalkTime

・・・これは表示が始まってからの経過時間。

f:id:nemes:20180909222558j:plain

TalkedAll

・・・これに全部表示したかの判定を入れる。

f:id:nemes:20180910222600j:plain

・Get Text Talk Text 0 このノードがバインドノードでイベントTickと一緒で一定間隔で呼び出される。そしてTalkIntervalが0だとブランチでtrueに流れる。

f:id:nemes:20180910214622j:plain

・ブランチのFalseに流れた場合。

TalkTimeをTalkIntervalで割りその答えをTruncateで小数点切捨てしている。LENノードでTalkerTextの文字数を取得。TalkTimeは一文字表示にかかる時間なので

経過時間÷1文字の時間

つまり「x秒経過したのでx文字表示」になる。

切り捨てた数字と表示文字数を比較して全文字表示したかを二つ目のブランチで判定。

f:id:nemes:20180910215124j:plain

 ・LeftノードはSource Stringの文字列をCountで取得した数分最初の数から数えてその文字を表示するってノード。(壊滅的日本語)

f:id:nemes:20180910220710j:plain

TalkedAllで全部表示したかを判定。

正直自分もいまいち理解してないから頑張って理解してくれ。

・イベントグラフはこれだけ

f:id:nemes:20180910222754j:plain

・れっつプレイ

f:id:nemes:20180911215127j:plain

もちろん画像じゃわからんけど成功。

・放置していた名前表示も仕上げる。

BP_TalkingActorを開き、以下のように変数を追加。コンパイルして名前も入力しておく。

f:id:nemes:20180912221105j:plain

・ThirdPersonCharacterのイベントグラフを開く。

Cast to BP_TalkingActorのAs BP Talking Actorのピンから今作ったBP_Nameを呼び出せるので配置。そんでTalk BoxからTalker Nameを呼び出せるのでセットして繋げる。f:id:nemes:20180912220637j:plain

これでウィジェットTalk Boxの変数Talker Nameに名前が格納される。そしてバインド関数のリターンノードにも自動的に名前が入る。 

f:id:nemes:20180913215910j:plain

出来たね 

・テキスト表示の仕上げをする。

まず三行分表示できるようにする。TalkBoxのイベントグラフ内で右クリックカスタムイベントを追加。

f:id:nemes:20180913220339j:plain

カスタムイベントの設定。

f:id:nemes:20180913220402j:plain

イベント Construct とくっつける。

f:id:nemes:20180913221757j:plain

Start TalkノードのTextが会話文になる。入力中に改行したい場合はShift + Enterでできるよ。 

・さらに会話終了の表示を出す。TalkBoxのデザイナー画面へ。遥か昔に作ったText_Cntnrを選択し、下にある緑色の+アニメーションボタンを押して新規アニメーションを追加。名前をContenueTextに。

f:id:nemes:20180913222643j:plain

そのまま右のタイムラインパネルで緑色の+Trackボタンを押してText_Cntnrを選択。選択したらVisibleのトラックを追加する。

f:id:nemes:20180913223210j:plain

 画像の様にプウルダウンでVisibleを設定。タイムラインで時間を0.00sに合わせて+ボタンを押す。そうするとタイムラインに白ポチが追加される。これでアニメーションは完了。

f:id:nemes:20180913223742j:plain

 ・ほんでグラフ画面に戻って会話文がすべて表示されたらアニメーションが起動するようにBPを書く。これは読んで字のごとくそのまま。

f:id:nemes:20180913224202j:plain

会話文が全て表示された判定をブランチで行い、Trueであれば Play Animationでアニメを再生する。まぁ再生って言ってもただ表示するだけだけど。

f:id:nemes:20180913225541j:plain

 よくできました。

 

 

 

というわけで今回の記事はここで終了。

投げっ放し&超絶改善の余地ありな出来だけど ゴールは達成できたのでおk。選択肢はいつかやる、いつかな。

さすがにダラダラ記事書いてて、これはいかんとということで最後は駆け足で雑になってしまった。次からは完成して公開できるクオリティになったら記事を書き始めるようにしよう・・・。

でも、やってることを記録に残すのはいいね、覚えてる気がする。

めざせ同人ゲーでお小遣い稼ぎ!!!!!

それじゃあね~~~~~~