独習 Unity アプリ開発

独習でスマフォ向けアプリ開発を勉強中

【UE5 C++】Unreal Engine で テトリス風ゲームを作ってみる#2

目次に戻る

前回のつづき


前回は、Unreal Engine でブロックを1つ表示させるところまで説明した。今回は、複数のブロックをまとめてテトリミノとして表示させるところまで確認する。

 Unreal Engine Version 5.3.2

 

カメラの設置


テトリミノを作る前に、その様子がPIE(Play in Editor : Play ボタンを押した状態)で確認ができるようにカメラを配置する。Unreal Engine でも最終的なゲーム画面はカメラで撮影された画像が表示されることになる。Unity だとProject を作成するとデフォルトでメインカメラがHierarchy 上に配置されているが、Unreal Engine だとどうもデフォルトでは作られないらしい(たぶん??)。そのため自分でカメラを作成する。

メニューバーの"Window" → "Place Actors" を選択し表示された"Place Actors" のタブで"ALL CLASSES"ボタンを押す。リストから"Camera Actor" の項目を探して、それをそのままLevel View 上にDrag & Drop するとカメラがLevel View 上に配置できる。

CameraActor の作成

 

配置したCamera Actor の位置と向きを調整するため、Detail タブの Location, Rotation, Projection Mode, Ortho Width, Auto Activate for Player のそれぞれの項目を下図のように変更する。

カメラの設定

 

Block Actor を座標 (0,0) の位置に配置してから、Level View 上のカメラアイコン(CameraActor)を選択すると、下図のようにカメラが撮影している画像が小窓で表示される。

カメラポジション

 

Unity と違って、カメラの画角は垂直方向の高さで指定するわけではなく、"Ortho Width" という設定で水平方向の幅で指定するらしい。画角の調整については別途確認するとして、一旦ここではテトリミノが表示できればよいので、適当な値にしておく。"Auto Activate for Player" の設定値はよく理解できていないが、これを"Disable" から "Player 0" に変更しておかないと、Play ボタンを押してゲームを開始したときに、設定したカメラ画像にならないため必須で変更する必要がある。

 

テトリミノの作成:アクターの親子関係


Unity では、GameObject の親子関係を使って(実際にはTransform)、親テトリミノと子ブロックの関係性を表現した。

Unity で テトリス風ゲームを作ってみる#2 - 独習 Unity アプリ開発

 

Unreal Engine でも同様のことが可能で、Actor::AttachToActor というメソッドで指定したアクターを自身の子にすることができる(実際にはUnreal Engine では、USceneComponent の親子関係になる)。

BluePrint エディタ上でどうやって親子関係を作るのかよくわからなったため、今回はC++ クラスを作って確認することとした。

 

まずは、親クラスとなるTetrimino クラスを準備。メニューバーの"Tool" → "New C++ Class" を選択。

CPP クラスを作成

 

作成するクラスの派生元を選択する画面が表示されるので、Actor クラスを選択する。

Actorクラス

 

クラス名を"Tetrimino" に変更して "Create Class" ボタンを押せば、C++クラスを作成される。

Tetrimino に名前を変更

 

作成されたC++ クラスは、"Content" フォルダの配下に"C++ Classes" というフォルダが自動で作られて、その下に置かれている(表示されていない場合は、一度Unreal Engine を起動しなおすか、Visual Studio でBuild し直すと表示される)。

Tetrimino クラス CPPファイル


今回Tetrimino クラスで実現することは、

  1. 初期化時にBlock BluePrint クラスを4つ生成する
  2. 生成したBlock BluePrint クラスを自分自身に子アクターとして登録する
  3. 各Block BluePrint クラスをテトリミノの種類に応じで配置する

の3つとする。Content ブラウザ上のTetrimino C++ クラスのアイコンをダブルクリックすると、Visual Studio が起動するのでそこでヘッダファイルとソースファイルを編集できる。

 

Tetrimino.h

  1. UCLASS()
  2. class TETRISLIKETEST1_API ATetrimino : public AActor
  3. {
  4.     GENERATED_BODY()
  5.     
  6. public:    
  7.     // Sets default values for this actor's properties
  8.     ATetrimino();
  9.  
  10. protected:
  11.     // Called when the game starts or when spawned
  12.     virtual void BeginPlay() override;
  13.  
  14. public:    
  15.     // Called every frame
  16.     virtual void Tick(float DeltaTime) override;
  17.  
  18.     // Declaration added
  19. public:
  20.     enum class EType : int { I, L, J, T, S, Z, O, MAX};    // kind of tetrimino
  21.     static const int BLOCK_NUM = 4;                         // number of block in tetrimino
  22.     static const int BLOCK_AIXS = 2;                          // x,y
  23.     static const int TETRIMINO[(int)EType::MAX][BLOCK_NUM][BLOCK_AIXS];
  24.     static const float BLOCK_SIZE;
  25.  
  26.     UPROPERTY(EditAnywhere)
  27.     TArray<AActor*> BlockActors;
  28.  
  29. private:
  30.     void Init();
  31.     void CreateBlocks(EType type);
  32.     void LocateBlocks(EType type);
  33.     TArray<FVector2D> GetBlockPositions(int type);
  34. };

 

Tetrimino.cpp

テトリミノの各種類ごとのブロック配置は、予めデータとして定義しておく。この辺りは、Unity での実装と同じ考え方を使う。

Block BluePrint クラスをC++ から扱えるように、まずはBP_Block BluePrint アセットをロードしてくる必要がある。これを実行しているのが、TSoftClassPtr::LoadSynchronous メソッド。TSoftClassPtr のコンストラクタにアセットのパス(FSoftObjectPath)を指定することで、BP_Block BluePrint アセットをロードできるようになる。

BP_Block BluePrint アセットのパス指定で注意が必要なのは、Content ブラウザ上では、"Content/TetrisLikeTest1/BluePrints/BP_Block" であるが、ここで指定するときは、"Content" を "Game" に、"BP_Block" を "BP_Block.BP_Block_C" に変更する必要があるということ。(そういうルールとして理解。深い理由は不明)

ロードしたBP_Block アセットをUWorld::SpawnActor に渡すことで、Block Actor クラスのインスタンスを生成することができる。次にUnity でやった時と同様に、各ブロックの位置をテトリミノの種類によって移動させる(Actor::SetActorLocation)

後は、インスタンス化したBlock Actor クラスからActor::AtachToActor を使って、Tetrimino の子Actor として登録することができる。Unity で言うところの、Transform.SetParent の意味と同じ(たぶん)。

  1. const int ATetrimino::TETRIMINO[(int)EType::MAX][BLOCK_NUM][BLOCK_AIXS] = {// tetrimino data
  2.     { // I
  3.         {0,0}, {1,0}, {2,0}, {3,0} // 0
  4.     },{ // L
  5.         {0,0}, {1,0}, {2,0}, {2,1} // 0
  6.     },{ // J
  7.         {0,0}, {1,0}, {2,0}, {0,1} // 0
  8.     },{ // T
  9.         {0,0}, {1,0}, {2,0}, {1,1} // 0
  10.     },{ // S
  11.         {0,0}, {1,0}, {1,1}, {2,1} // 0
  12.     },{ // Z
  13.         {1,0}, {2,0}, {0,1}, {1,1} // 0
  14.     },{ // O
  15.         {0,0}, {1,0}, {0,1}, {1,1} // 0
  16.     }
  17. };
  18.  
  19.  
  20. const float ATetrimino::BLOCK_SIZE = 100.0f;
  21.  
  22.  
  23. // Sets default values
  24. ATetrimino::ATetrimino()
  25. {
  26. // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
  27.     PrimaryActorTick.bCanEverTick = true;
  28. }
  29.  
  30.  
  31. // Called when the game starts or when spawned
  32. void ATetrimino::BeginPlay()
  33. {
  34.     Super::BeginPlay();
  35.     Init();
  36. }
  37.  
  38.  
  39. // Called every frame
  40. void ATetrimino::Tick(float DeltaTime)
  41. {
  42.     Super::Tick(DeltaTime);
  43. }
  44.  
  45.  
  46. void ATetrimino::Init() {
  47.     CreateBlocks(EType::L);
  48. }
  49.  
  50. void ATetrimino::CreateBlocks(EType type) {
  51.     
  52.  // Folder name should be Game instead of Content    
  53.     FString path =
  54.         FString(TEXT("/Game/TetrisLikeTest1/BluePrints/BP_Block.BP_Block_C"));
  55.     
  56.     TSubclassOf<class AActor> blockbp =
  57.         TSoftClassPtr<AActor>(FSoftObjectPath(*path)).LoadSynchronous();
  58.  
  59.     if (blockbp != nullptr)    {
  60.         for (int i = 0; i < BLOCK_NUM; i++) {
  61.             AActor* pBlockActor = GetWorld()->SpawnActor(blockbp);    
  62.             pBlockActor->SetActorLocation(FVector(0, 0, 0));
  63.             pBlockActor->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform);
  64.             BlockActors.Add (pBlockActor);
  65.         }
  66.         LocateBlocks(type);
  67.     }
  68. }
  69.  
  70. void ATetrimino::LocateBlocks(EType type) {
  71.     TArray<FVector2D> blockPositions = GetBlockPositions( (int)type);
  72.     for (int i = 0; i < BLOCK_NUM; i++) {
  73.         TObjectPtr<USceneComponent> pSceneComponent = BlockActors[i]->GetRootComponent();
  74.         pSceneComponent->SetRelativeLocation(
  75.             FVector(
  76.                 blockPositions[i].X * BLOCK_SIZE,
  77.                 0,
  78.                 blockPositions[i].Y * BLOCK_SIZE));
  79.     }
  80. }
  81.  
  82. TArray<FVector2D> ATetrimino::GetBlockPositions(int type) {
  83.     TArray<FVector2D> blockPosition;
  84.     for (int i = 0; i < BLOCK_NUM; i++) {
  85.         blockPosition.Add(FVector2D(TETRIMINO[type][i][0], TETRIMINO[type][i][1]));
  86.     }
  87.     return blockPosition;
  88. }

 

 

ヘッダとソースファイルを保存したら、Unreal Engine 上のコンパイルボタンを押してコンパイルを実行。

コンパイルボタン

 

次は、Tetrimino C++ クラスからBluePrintを作成する。Tetrimino C++ クラスには、USceneComponent がついていないため、このままLevel View に配置して実行しても、子Block BluePrint が紐づかない(親子関係はあくまでUSceneComponent によって実現されるため)。そこでBluePrint 化することでUSceneComponent をアタッチするようにする。Actor BluePrint はデフォルトでUSceneComponent を持っている状態で生成されるため。

Content ブラウザ上のTetrimino C++ クラスを右クリックし、メニューから"Create Blueprint class based on Tetrimino" を選択する。

Tetrimino C++ クラスからBluePrint を作成

 

名前と保存先を聞かれるので、"BP_Tetrimino" として"BluePrints" フォルダを指定。

名前とパスを設定

 

出来たBP_Tetrimino BluePrintのアイコンをLevel View の座標(0,0) にDrag & Drop する。

Play ボタンを押すと、狙い通りにL 型のテトリミノが表示された。ちゃんとBP_Tetrimino の下階層にBP_Block が4 つ並んでいることも確認できる(Unity と同様)。

テトリミノの表示

 

次の記事


Unreal Engine で テトリス風ゲームを作ってみる#3 - 独習 Unity アプリ開発

 

目次に戻る