【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 上に配置できる。
配置した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" を選択。
作成するクラスの派生元を選択する画面が表示されるので、Actor クラスを選択する。
クラス名を"Tetrimino" に変更して "Create Class" ボタンを押せば、C++クラスを作成される。
作成されたC++ クラスは、"Content" フォルダの配下に"C++ Classes" というフォルダが自動で作られて、その下に置かれている(表示されていない場合は、一度Unreal Engine を起動しなおすか、Visual Studio でBuild し直すと表示される)。
今回Tetrimino クラスで実現することは、
- 初期化時にBlock BluePrint クラスを4つ生成する
- 生成したBlock BluePrint クラスを自分自身に子アクターとして登録する
- 各Block BluePrint クラスをテトリミノの種類に応じで配置する
の3つとする。Content ブラウザ上のTetrimino C++ クラスのアイコンをダブルクリックすると、Visual Studio が起動するのでそこでヘッダファイルとソースファイルを編集できる。
Tetrimino.h
- UCLASS()
- class TETRISLIKETEST1_API ATetrimino : public AActor
- {
- GENERATED_BODY()
- public:
- // Sets default values for this actor's properties
- ATetrimino();
- protected:
- // Called when the game starts or when spawned
- virtual void BeginPlay() override;
- public:
- // Called every frame
- virtual void Tick(float DeltaTime) override;
- // Declaration added
- public:
- enum class EType : int { I, L, J, T, S, Z, O, MAX}; // kind of tetrimino
- static const int BLOCK_NUM = 4; // number of block in tetrimino
- static const int BLOCK_AIXS = 2; // x,y
- static const int TETRIMINO[(int)EType::MAX][BLOCK_NUM][BLOCK_AIXS];
- static const float BLOCK_SIZE;
- UPROPERTY(EditAnywhere)
- TArray<AActor*> BlockActors;
- private:
- void Init();
- void CreateBlocks(EType type);
- void LocateBlocks(EType type);
- TArray<FVector2D> GetBlockPositions(int type);
- };
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 の意味と同じ(たぶん)。
- const int ATetrimino::TETRIMINO[(int)EType::MAX][BLOCK_NUM][BLOCK_AIXS] = {// tetrimino data
- { // I
- {0,0}, {1,0}, {2,0}, {3,0} // 0
- },{ // L
- {0,0}, {1,0}, {2,0}, {2,1} // 0
- },{ // J
- {0,0}, {1,0}, {2,0}, {0,1} // 0
- },{ // T
- {0,0}, {1,0}, {2,0}, {1,1} // 0
- },{ // S
- {0,0}, {1,0}, {1,1}, {2,1} // 0
- },{ // Z
- {1,0}, {2,0}, {0,1}, {1,1} // 0
- },{ // O
- {0,0}, {1,0}, {0,1}, {1,1} // 0
- }
- };
- const float ATetrimino::BLOCK_SIZE = 100.0f;
- // Sets default values
- ATetrimino::ATetrimino()
- {
- // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
- PrimaryActorTick.bCanEverTick = true;
- }
- // Called when the game starts or when spawned
- void ATetrimino::BeginPlay()
- {
- Super::BeginPlay();
- Init();
- }
- // Called every frame
- void ATetrimino::Tick(float DeltaTime)
- {
- Super::Tick(DeltaTime);
- }
- void ATetrimino::Init() {
- CreateBlocks(EType::L);
- }
- void ATetrimino::CreateBlocks(EType type) {
- // Folder name should be Game instead of Content
- FString path =
- FString(TEXT("/Game/TetrisLikeTest1/BluePrints/BP_Block.BP_Block_C"));
- TSubclassOf<class AActor> blockbp =
- TSoftClassPtr<AActor>(FSoftObjectPath(*path)).LoadSynchronous();
- if (blockbp != nullptr) {
- for (int i = 0; i < BLOCK_NUM; i++) {
- AActor* pBlockActor = GetWorld()->SpawnActor(blockbp);
- pBlockActor->SetActorLocation(FVector(0, 0, 0));
- pBlockActor->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform);
- BlockActors.Add (pBlockActor);
- }
- LocateBlocks(type);
- }
- }
- void ATetrimino::LocateBlocks(EType type) {
- TArray<FVector2D> blockPositions = GetBlockPositions( (int)type);
- for (int i = 0; i < BLOCK_NUM; i++) {
- TObjectPtr<USceneComponent> pSceneComponent = BlockActors[i]->GetRootComponent();
- pSceneComponent->SetRelativeLocation(
- FVector(
- blockPositions[i].X * BLOCK_SIZE,
- 0,
- blockPositions[i].Y * BLOCK_SIZE));
- }
- }
- TArray<FVector2D> ATetrimino::GetBlockPositions(int type) {
- TArray<FVector2D> blockPosition;
- for (int i = 0; i < BLOCK_NUM; i++) {
- blockPosition.Add(FVector2D(TETRIMINO[type][i][0], TETRIMINO[type][i][1]));
- }
- return blockPosition;
- }
ヘッダとソースファイルを保存したら、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" を選択する。
名前と保存先を聞かれるので、"BP_Tetrimino" として"BluePrints" フォルダを指定。
出来たBP_Tetrimino BluePrintのアイコンをLevel View の座標(0,0) にDrag & Drop する。
Play ボタンを押すと、狙い通りにL 型のテトリミノが表示された。ちゃんとBP_Tetrimino の下階層にBP_Block が4 つ並んでいることも確認できる(Unity と同様)。
次の記事
Unreal Engine で テトリス風ゲームを作ってみる#3 - 独習 Unity アプリ開発