UE5でFPSゲーム制作 Part26-NPCに話しかける
イベントディスパッチャーを使って話しかける
相手に対してアクションを起こすときはブループリントインターフェースを通したメッセージか、イベントディスパッチャーを通して呼び出すかの2つが使われやすいです。
この2つを簡単に分けると、インターフェースは発信者が相手を知っている時にアクションを起こし、ディスパッチャーは受信者が相手を知っている時にアクションを起こすというイメージです。
まずはPlayerCharacterにイベントディスパッチャーを追加します。
PlayerCharacter.h
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FInteractEvent);
UPROPERTY(BlueprintAssignable, Category = "Action")
FInteractEvent OnInteractEvent;
UFUNCTION(BlueprintCallable, Category = "Action")
void OnInteractEventDispather();
PlayerCharacter.cpp
void APlayerCharacter::OnInteractEventDispather()
{
OnInteractEvent.Broadcast();
}
BP_PlayerCharacter
BP_PlayerCharacterでインタラクトアクション用のIA_InteractからOn Interact Event Dispatcherノードにつなぎます。
Actor型のHit Item変数が武器かどうかを判断しているので、Actorが武器でない場合はイベントディスパッチャーを呼びます。
以前武器を拾うためにTraceItemというトレースチャンネルを作りましたが武器だけでなく話しかけるなどのアクション全般を担当するので名前を変更しておきます。
プロジェクト設定のコリジョンからTraceItemというトレースチャンネルをTraceInteractと変更します。
つぎにNPCを作っていきます。
新規BPクラスからCharacterを選択し、BP_TalkNPCと名付けて作成します。
BP_TalkNPC
Capsule ComponentのコリジョンレスポンスからTraceInteractのトレース応答をブロックにしておきます。
またSphereコリジョンを追加します。Sphere Radiusを350にし、コリジョンレスポンスはPawnのみにしておきます。
このSphereからOn Component Begin Overlapというイベントを作成します。
On Component Begin Overlapイベントから、PlayerCharacterへキャストし、On Interact Eventにイベントをバインドします。これでプレイヤーがインタラクトしたときにInteractというカスタムイベントを呼ぶことができます。
また、複数のNPCがいる場合、バインドがどんどん増えていくため、会話後にバインドを解除します。ただ、この場合、一度会話をした相手にもう一度話しかけるには一度Sphereの範囲外にいかないといけないので、連続で話しかけられるようにするにはEnd Overlap Eventにバインドを解除をいれるなど少し工夫が必要です。
各NPCごとに会話を変えるにはTextIndexをインスタンス上で編集できるようにすればできます。
UE5でFPSゲーム制作 Part25-翻訳
今回はC++からではなく直接入力する方法で翻訳します。個人制作の範囲なら翻訳を別の人が担当するということは少ないと思います。
参考になるサイト
最新UE4タイトルでのローカライズ事例 【UE4 Localization Deep Dive 2019】 | ドクセル (docswell.com)
テキストの翻訳
翻訳にはローカライゼーションダッシュボードを使用します。
まずは、その他からストリングテーブルを選択します。ST_Widgetと名付けます。ストリングテーブルはUIや字幕、アイテムリスト等で分けておくと後で把握しやすいかなと思います。
ST_Widget
ストリングテーブルではネームスペースとキー、ソースストリングを設定します。完全に自分も把握してるわけではないのですが
ネームスペース:STごとに一つだけ設定するタグ。
キー:翻訳対象を設定するためのキー。(システム言語的なやつ)
ソースストリング:翻訳前のデフォルト言語として使われる文字列。
という感じで使います。右下の追加でキーを追加します。
ローカライゼーションダッシュボード
ツールからローカライゼーションダッシュボードを開きます。
パッケージから収集でInclude Path Wildcardsにストリングテーブルを作ったフォルダを指定します。「Content/フォルダ階層/*」で指定します。
一番下の新規カルチャーを追加で翻訳したい言語を指定します。今回は日本語を追加します。
(ネイティブ「青い丸のところ」には使用しない言語をデフォルト言語として設定するのが良いとのこと。今回は日本語に翻訳するだけなのでネイティブは英語のままにしてます。)
「テキストを収集する」というアイコンを押すとストリングテーブルから収集されます。
デフォルト言語である英語は100%ですが、収集された未翻訳の言語がある日本語はワード数が変わらないままです。
日本語のアクションから「このカルチャーの翻訳を編集」を選択します。
未翻訳のなかに先ほどストリングテーブルに追加したソースストリングがあるので、Translationのところに翻訳した言語を入力して保存します。
ローカライゼーションダッシュボードに戻って「ワード数をカウントする」を押すと、テキストを再度収集して日本語のワード数が100%になりました。最後に「テキストをコンパイル」を押します。
あとは対応するキーをウィジェットやBP側で設定します。
WBP_Titleに設定したStartというテキストを翻訳してみます。変数等を使用しないテキストを翻訳するときは、Text上から直接編集できます。
Textウィジェットを選択し、右のTextのところにある旗のマークをクリックするとローカライズの画面が出てくるので、ストリングテーブルからST_Widgetを選択すると、キーを選択できるようになるのでStartGameのキーを設定します。
これで翻訳ができました。
ゲームプレビュー上の言語を変更するにはエディタの環境設定→地域&言語でゲームのプレビュー言語を変更します。
エディタ上でも翻訳された言語を確認するには、ウィジェットBPの真ん中付近にある地球のマークからjaを選択したり、日本語の翻訳エディタのところでエディタでプレビューをクリックしたりしてみてください。
(ここ反映されたりされなかったりするのですが原因がよくわかりませんでした。)
BP上で翻訳する
WBP_MainHUDの武器の名前など、変数で表示する部分はブループリント上で翻訳する必要があります。
String型をText型に変換するToTextノードを消して、Find Text in Localization Tableノードに変更します。
NamespaceはST_Widgetに設定したネームスペースにし、武器の名前をKeyに割り当てます。Keyはスペースを入力できないので、Weapon Name変数はRifleA、SmgBのように変更する必要があります。
UE5でFPSゲーム制作 Part24-字幕
字幕
字幕が呼ばれるタイミングは時間、場所、アクションなど様々なフラグによって違うため、World Subsystemを利用するとどこからでもアクセスできて便利です。
BattleManagerSubsystem.h
作成したBattle Manager SubsystemはWorld Managerなのでここにイベントディスパッチャーを作成します。
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSubtitlesTextEvent, int, text, int, color);
UPROPERTY(BlueprintAssignable, Category = "BattleManager")
FSubtitlesTextEvent OnSubtitlesTextEvent;
UFUNCTION(BlueprintCallable, Category = "BattleManager")
void OnSubtitlesTextEventDispather(int TextIndex, int ColorIndex);
BattleManagerSubsystem.cpp
テキスト番号とテキストカラーの2つのパラメーターを送るようにします。
WBP_MainHUD
ウィジェットにHorizonal Boxで字幕用のテキストを作ります。
ConstructからOn Subtitles Text Eventにイベントをバインドします。
Elate Visibilityの型とint型の変数を用意します。Visibility SubtitlesはデフォルトでHiddenであり、呼ばれると5秒間Visibleになります。
先ほど作った字幕のテキストにバインディングを作成します。
名前とテキストはそれぞれ別のバインディングを作成し、Subtitles_Text_とSubtitles_Name_で区別します。イベントディスパッチャーが呼ばれると、テキストから参照されて、「Subtitles_Name_0:Subtitles_Text_0」」という字幕を表示し0の部分が変化していきます。
名前のほうのテキストにColor and Opacityのバインディングを作成します。
リターンノードの構造体ピンを分割してColorを編集します。イベントディスパッチャーのColorという変数でスイッチします。敵のセリフを赤にしたり、味方のセリフを青にしたりといった使い方ができます。
あとは任意の場所からイベントディスパッチャーを読んであげるだけです。
テキストの翻訳は別に行う必要がありますが、字幕の作成自体はこれで完成です。
UE5でFPSゲーム制作 Part23-タイトル画面
タイトル画面とステージ遷移
UMGを使ってタイトルからステージへ移行するには、Open Levelを使います。
タイトル用の何もない真っ暗な新規レベルを作成します。
次に新規ブループリントからGame Mode Baseを選択し、BP_TitleGameModeと名付けます。
BP_TitleGameMode
Default Pawn ClassはNoneに設定します。
エディタのワールドセッティングからUse Battle Managerをfalseにし、ゲームモードをBP_TitleGameModeに設定します。
新規のウィジェットBPを作成し、WBP_Titleと名付けます。
適当に画面を作成したら、スタート用のButtonウィジェットを配置します。
次にBP_TitleGameModeにカスタムイベントを作成します。
BP_TitleGameMode
Show Main Menuというカスタムイベントを作成します。
WBP_Title型の変数を作成します。最初のノードは認証済みゲットといってGet WBP_Titleを右クリックから変換できます。
Set Input Mode OnlyはUIのみを制御し、Add ViewportでWBP_Titleウィジェットを表示します。
Show Mouse Cursorはマウスの表示オンオフです。
次にエディタの左上からTitleレベルのレベルブループリントを開きます。
タイトルが読み込まれたらBP_TitleGameModeへキャストし、Show Main Menu関数を呼び出します。
(Delayのように時計のマークがついたノードは、処理が終わるまで後続の処理をストップするlatentノードといいます。確実にShow Main Menu関数を呼ぶために使っています。)
起動するとタイトル画面が表示されました。
次にWBP_Titleからスタートボタンを設定します。
WBP_Title
Set Input Mode Game Onlyでゲーム画面を動かせるようにし、マウスを消します。
Play UMGLoading Screenは前回紹介した非同期ロードなのでなくても大丈夫です。
これで、タイトル画面からゲーム画面へステージ移行できるようになりました。
UE5でFPSゲーム制作 ローディング画面
今回は参考になった他者様のブログ紹介です。
ステージ等の切り替えの際、普通にOpen Levelでレベルを開くとロードが終わるまで画面が完全に固まってしまいます。
「画面が完全に止まった状態」というのはゲームを遊ぶプレイヤーに対してかなりのストレスを与えてしまうそうです。真っ暗な画面でも左下のLoading...の「...」が動いているだけでかなり違うそうですね。
そこで画面が止まらない非同期ローディングを作る必要があります。
が、初心者の自分では作り方が全然わからなかったので、わかりやすく解説してくれている方を紹介します。
UE4 もっとローディング画面➀ - Suutaの秘密基地 (hatenablog.com)
補足:
MoviePlayerとUMGをC++で使用するにはBuild.csにモジュールを登録しておく必要があります。
PrivateDependencyModuleNames.AddRange(new string[] { "MoviePlayer", "UMG" });
(ここ調べたサイト等によってPublicMuduleかPrivateModuleかどちらに登録するかが違うのですが違いがよくわかりませんでした...)
完成したBP_GameIncetanceは、プロジェクト設定のマップ&モードから忘れずにゲームインスタンスクラスに登録しておきます。
UE5でFPSゲーム制作 Part22-スタートとゴール
前々回Battle Manager SubsystemにEnemy Countという変数を作りました。ワールド内の敵キャラクターがやられると、この数字は減っていきます。そしてこの数字が0になったときに、通知がいくようになればゴールが作れます。
イベントディスパッチャー
1つ以上のイベントをイベントディスパッチャーへ結合すると、イベントディスパッチャーの呼び出し時にこれらのイベントを一度に発行できます。
ゴール時には音楽やUIなどいろいろなクラスを呼ぶ可能性が高いので、ゴール時用のイベントディスパッチャーを作っておきます。
BattleManagerSubsystem.h
BattleManagerSubsystem.cpp
On Enemy ClearというイベントとOn Stage Clearというイベントを作成します。
Enemy CountはAddやSetで調整することができ、0になったときはEnemy Clearが呼ばれます。
FPSゲームにおいて敵の撃破がフラグとなる場合というのはとても多いと思います。Reduceのタイミングで新しいイベントを用意するなどすれば、増援やイベント進行など様々な使い方ができるものになります。
BP_StartArea
新規BPからActorを選択し、BP_StartAreaと名付けます。
Boxコリジョンを親とするこのアクターからOverlapイベントでSet Enemy Countを登録します。
新規BPからActorを選択し、BP_StageManagerと名付けます。
BP_StageManager
On Enemy Clear EventとOn Stage Clear Eventにイベントをバインドします。
追加の敵やイベントが発生しないので、Enemy ClearのカスタムイベントからStage Clearを呼び出します。
敵キャラクターとBP_StartAreaとBP_StageManagerをレベル上に配置することで、ゲームのスタートからクリアまでの流れがひとまず完成することになります。
UE5でFPSゲーム制作 Part21-体力とコンポーネントの追加
Apply Damageで銃からダメージを与えると、Any Damageというイベントでダメージを数字として受け取ります。
ステータスを作る
新規C++クラスからActor Componentを選択し、StatusComponentと名付けます。
StatusComponent.h
StatusComponent.cpp
CharacterBase.h
CharacterBaseクラスにStatusComponentクラスを追加します。TakesDamage関数とOnDied関数を用意します。
TakesDamage関数はダメージの処理を行っており、体力が0になったらOnDied関数が呼ばれます。OnDiedはBlueprintImplementableEventなので中身はブループリントで書いていきます。
新規ブループリントクラスからEnemyCharacterを継承してBP_EnemyDollというキャラクターを作ります。
BP_EnemyDoll
ダメージを受けると、Takes Damage関数でダメージの計算が行われたあと、体力が0になったらOn DiedイベントからBattle Manager SubsystemのReduce Enemy Countを呼びます。
Subsystemはどこからでも呼び出せるためBPクラスの参照依存を作らずに、ゲームシステムにアクセスすることができます。