【UE5 C++】Unreal Engine で テトリス風ゲームを作ってみる#8
前回のつづき
前回は、テトリミノ着地後のライン削除処理について確認する。今回は、レベルとスコア、UIについて確認する。
Unreal Engine Version 5.3.2
UMG(Unreal Motion Graphics)
レベルとスコアを計算するロジック部分の実装は、Unity とUnreal Engine でそれほど差分はない。問題はUI である。
Unity ではuGUI というUI システムがあり、Hierarchy ウィンドウ上から各種UI コンポーネントを生成することで、比較的簡単にUI を実装できる。例えば、テキストコンポーネントの文字列をスクリプトから変更したい場合などは、"SerializeField" を使って事前に該当のコンポーネントオブジェクトを設定しておけば、実行時にスクリプトからテキストコンポーネントのテキストメンバに直接アクセスして文字列を書き換えることができる。
ゲーム画面上でレベルやスコアを実現するには、少なくとも表示しているUI のテキストコンポーネントにC++ クラスからアクセスして文字列を書き換えるということが実現できなければならない。
Unreal Engine でもUMG というUI システムを使ってUI を実現する。UMG は、Widget Blueprint クラスと、Widget と呼ばれる各種UI コンポーネントから構成される。簡単に言うと、Widget Blueprint を使って各UI Widget を配置し、C++ クラスからActor Blueprint と同様にインスタンスを生成して画面に描画する。また、生成したインスタンスからUI Widget インスタンスを取得して、そのメンバを書き換えることで、UI 上のテキストなどをC++ クラスから変更することができる。
では、まずは、Widget Blueprint クラスを作成する。Content Browser 上で、"UMG" というフォルダを作成し、作成した空のフォルダ上で右クリックから"User Interface"→"Widget Blueprint" を選択する。"Widget Blueprint" クラスが作成されるので、作成したWidget Blueprint の名前を"WBP_LevelScore"に変更する。Widget Blueprint クラスのプリフィックスは"WBP_"
次に作成した"WBP_LevelScore" のアイコンをダブルクリックして、Widget Blueprint Editor を起動する。Widget Blueprint Editor では、各種UI Widget を視覚的にレイアウトすることができる。
今回は、Level とScore を表示するUIを作成する。まずはUI Widget の下地になる"Canvas Panel" をPalette のリスト(画面右側)から選択して、そのまま画面中央のグリッドにDrag & Dropする(緑の枠線がCanvas Panel)。
今度は、"Canvas Panel" 上に、"Horizontal Box"、その中に"Vertical Box"、その中に"Text" を入れ子状に上図のように配置する。うまくいかないときは、画面左下にある "Hierarchy" を使って入れ子状態を整理する。UI レイアウトが完成したら、"Compile" して "Save" するのを忘れずに実行し、Blueprint Editor を閉じる。
次にLevel とScore を管理するC++ クラスを作成する。このクラスを直接画面上に配置するわけはないのでActor ではなくUObject の派生クラスとして生成する。
メニューから"Tools"→"New C++"を選択し、"All Class" ボタンを押して、すべての派生元クラス一覧を出す。その中から"Object" を選択してクラスを作成する。(Object が UObject のこと)。
を使ってWidget と呼ばれる各種UI コンポーネント(UWidget クラスから派生したクラス)をEditor 上から配置し、Blueprint を使って制御することができる。もちろん、Actor Blueprint クラスのように、C++ クラスからWidget Blueprint のインスタンスをロードして、制御することも可能。
LevelScore.cpp の抜粋
まずは、Widget Blueprint クラスのインスタンスを生成するところは、Actor Blueprint と同様。Blueprint のパスをFSoftObjectPath に入れて、TSoftClassPtr のLoadSynchronous メソッドでロードする。ロードできたクラスをUUserWidget::CreateWidgetInstance メソッドを使ってインスタンス化し、できたUUserWidget インスタンスのAddToViewPort を呼び出して画面に表示する。ちなみにWidget Blueprint でレイアウトしたUI Widget は、UUserWidget クラスとして定義される。UUserWidget も Widget Blueprint でレイアウトした各UI Widget もUWidget クラスが親クラスになっている。
- void ULevelScore::Init()
- {
- // Load Widget
- auto path =
- FString(TEXT("/Game/TetrisLikeTest2/UMG/WBP_LevelScore.WBP_LevelScore_C"));
- auto widgetClass =
- TSoftClassPtr<UUserWidget>(FSoftObjectPath(*path)).LoadSynchronous();
- auto userWidget = UUserWidget::CreateWidgetInstance(*GetWorld(), widgetClass, NAME_None);
- userWidget->AddToViewport(0);
- LevelTextBlock = Cast<UTextBlock>(userWidget->GetWidgetFromName(TEXT("LevelText")));
- ScoreTextBlock = Cast<UTextBlock>(userWidget->GetWidgetFromName(TEXT("ScoreText")));
- }
最後に、UUserWidget のインスタンから、TextBlockのインスタンスを取得する。これには、UUserWidget::GetWidgetFromName メソッドを使って、Widgetの名前から取得することができる。Widget の名前は、Widget Blueprint Editor 上で名前を付けたいUI Widget を選択して右側にあるDetails タブで自由に変更することができる。
今回、Level の値を表示するText Widget には、"LevelText"。Socre の値を表示するText Widget には、"ScoreText" と名前を付けておく。
最後は、Text Widget の文字列をC++ クラスから変更する処理について。
LevelScore.cpp の抜粋
上記で取得しておいたTextBlock のインスタンから、SetText メソッドを使えば、指定した文字列に変更することができる。この辺りの考え方はUnity と同様である。
- void ULevelScore::UpdateLevelText()
- {
- LevelTextBlock->SetText(FText::FromString(FString::Printf(TEXT("%02d"), Level)));
- }
- void ULevelScore::UpdateScoreText()
- {
- ScoreTextBlock->SetText(FText::FromString(FString::Printf(TEXT("%06d"), Score)));
- }
ここまでプロトした結果がこちら。テトリスの腕がいまいちなので、Level とScore が上がるのに時間がかかって申し訳ないが、一応UI の更新もできていることが確認できる。
次の記事