[UE5] Lyra改造計画④-2 ワールドに配置したPlayerStartから、ボットを生成して、個々のアクターを設定する

Post 2023年5月21日日曜日

C++ GASでオンラインシリーズ Lyra改造 UE5 Unreal Engine オンラインゲーム

 前回の続き。

お約束
この記事作成にあたって使用した主なUnreal Engine バージョンUE 5.1.1

本日のゴール

LyraでワールドにPlayerスタートを配置すると、自動的にBotが生成するように設定する


Botのアクター選択

Bot生成に関して、下記のように担当が分かれています。

① Bot 生成時、どのPlayerStartでSpawnするか? → 前述の通り、「LyraPlayerSpawningManagerComponent」の「ChoosePlayerStart」関数

② Bot 生成時、どのAI Controllerを使うか? → 
「LyraBotCreationComponent」の「Bot Controller Class」で指定し、「ServerCreateBots」及び「SpawnOneBot」で生成

③ Bot 生成時、どのActorを使うか? → 
「LyraControllerComponent_CharacterParts」の「Add Character Part」で指定 *実際にはB_PickRandomCharacterというブループリントが継承しているのでこれを使えばOK

LyraではAI Controllerは一種類、Actorは2種類しか使っていません。このため非常に楽に制御ができましたが、私のゲームでは5種類くらいは使いたいと思っているので、何らかの手段で効率的に管理しないといけません。

こんなとき!便利なのがData Tableです。最終的にはData Tableとかを使っていきますが、まずはそれはあとにして動作を確認していきます。(オイ)


どういうことをするのか?

③の「B_PickRandomCharacter」でGet Ownerを使うと、Controllerが取り出せるので、そこからなら情報が取り出せそうな感じがします。

そのコントローラー内に、情報を格納しておくようにします。

そのコントローラーは、②の「LyraBotCreationComponent」で生成するので、そのときに情報を渡してやればいいわけです。

というわけで、「LyraBotCreationComponent」でAIコントローラー「LyraPlayerBotController」をスポーンするときに、同時にBotのクラスを渡してあげるようにします。


目指す図は上記になっています。なんでこんなに面倒くさい手順にするかというと、各Botで保存する自分のBotクラスを変えたいからですね。

LyraPlayerBotController.h」のPublic:直下

  1.     /** Bot Class Num to specify PlayerStart */
  2.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Bot", meta = (ExposeOnSpawn = true))
  3.     int BotClassNum;

まずはBotClassNumというint変数を用意してあげます。


これをSpawn時に渡してあげたいので、

LyraBotCreationComponent.cpp」の「SpawnOneBot()」内に追加

  1.     FActorSpawnParameters SpawnInfo;
  2.     SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
  3.     SpawnInfo.OverrideLevel = GetComponentLevel();
  4.     SpawnInfo.ObjectFlags |= RF_Transient;
  5.     AAIController* NewController = GetWorld()->SpawnActor<AAIController>(BotControllerClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnInfo);
  6.     
  7. //下記追加

  8.     if (ALyraPlayerBotController* NewBotCon = Cast<ALyraPlayerBotController>(NewController)) {
  9.         NewBotCon->BotClassNum = 1;
  10.     }

ALyraPlayerBotControllerにCastするために、ALyraPlayerBotControllerを読み込んでおきます。

LyraBotCreationComponent.cpp」の一番初め

  1. #include "Player/LyraPlayerBotController.h" // 追加

上記の例では、LyraGameInstanceからGetするのは一旦飛ばして、とりあえず1で指定しています。


また、オプションですが「LyraPlayerSpawningManagerComponent.cpp」でLyraPlayerBotContorollerからBotClassを読んでおきます。

前回のコードの中の、Botに関する部分を変更します。

  1.         else if (!(Player->IsPlayerController())) {
  2.             if (ALyraPlayerBotController* BotControllerDayo = Cast<ALyraPlayerBotController>(Player)) {
  3.                 int botclassnumref = BotControllerDayo->BotClassNum;
  4.                 for (auto StartIt = CachedPlayerStarts.CreateIterator(); StartIt; ++StartIt)
  5.                 {
  6.                     if (ALyraPlayerStart* Start = (*StartIt).Get())
  7.                     {
  8.                         if (Start->GetLocationOccupancy(Player) == ELyraPlayerStartLocationOccupancy::Empty) {
  9.                             if (Start->BotPlayerStartTag == FName(TEXT("Bot"))) {
  10.                                 if (Start->BotClass == botclassnumref) {
  11.                                     PlayerStart = Start;
  12.                                     break;
  13.                                 }
  14.                             }
  15.                         }
  16.                     }
  17.                 }
  18.             }
  19.         }

何がなんだかって感じですが、やってることは単純で、BotControllerが持っているBotClassと、PlayerStartが持っているBotClassが一致するときにだけ、そのPlayerStartを使ってSpawnするということです。

要するに、AというBotはA というPlayerStartしか使えなくなるというだけです。


あとは簡単だよね

あとは「B_PickRandomCharacter」でLyraPlayerBotControllerにCastして、情報を取り出すだけですね。

こんな感じで、LyraPlayerBotControllerにCastして、そこからBotClassを取り出し、BotClassによってアクターを選択できるようにします。

めでたしめでたし・・・と思ったら・・・


常に0になるじゃん

実はLyraPlayerBotControllerにBot Classが設定される前に、上記ブループリントが働いてしまうのでこのような結果になります。

こういった場合はいくつか対処手段がありますが、一番正攻法のEvent Dispatcherを使ってみましょう。


まず、「LyraPlayerBotController.h」の#include郡の下辺りにこれを追加します。デリゲートです。

  1. DECLARE_DYNAMIC_MULTICAST_DELEGATE(FChangeBotNum);

そしたら、次にPublic:直下に下記を追加します。

  1. /** Register Delegate */
  2. UPROPERTY(BlueprintAssignable)
  3. FChangeBotNum OnChangenumevent;

次に、「LyraBotCreationComponent.cpp」の「SpawnOneBot()」内に追加した内容を修正

  1.     //下記追加
  2.     if (ALyraPlayerBotController* NewBotCon = Cast<ALyraPlayerBotController>(NewController)) {
  3.         NewBotCon->BotClassNum = 1;
  4.         NewBotCon->OnChangenumevent.Broadcast(); //さらに追加
  5.     }

デリゲートを呼び出してやります。

あとは「B_PickRandomCharacter」でEvent Dispatcherを使うだけです。


Castが失敗する場合は、つまりPlayerのキャラということなので、
Playerが使いたいキャラクターを設定します。



こんな感じで、プレイヤーとBotでキャラクターを変えることができました!しかもBotはクラスが違えばキャラクターを変えるように設定することもできます。

ちなみにですが、銃を失わせるには「B_Hero_ShooterMannequin」で下記のノードを外してやればいいです。なので、Bot ClassによってSwitchさせてやればいいですね。ここはまたどこかで詳しくやりたいな…やれるかな…


ブループリントでは多分一瞬でできる実装も、C++だと結構たいへんですね…(心の悲鳴)。

とりあえず今回は以上です。次はOnline Subsystem周りをやり始めます。いよいよですなぁ…。