ゲーム開発
Unity
UnrealEngine
C++
Blender
Houdini
ゲーム数学
ゲームAI
グラフィックス
サウンド
アニメーション
GBDK
制作日記
IT関連
ツール開発
フロントエンド関連
サーバサイド関連
WordPress関連
ソフトウェア設計
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
ラーメン日記
四コマ漫画
その他
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • Blender
    • Houdini
    • ゲーム数学
    • ゲームAI
    • グラフィックス
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • IT関連
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • WordPress関連
    • ソフトウェア設計
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • ラーメン日記
    • 四コマ漫画
      • その他
        • おすすめアイテム
        • おもしろコラム
      1. ホーム
      2. 20240519_03_ue5_cpp_ui_effect

      【UE5】第三回 ミニゲーム制作で学ぶUnrealC++ 〜UI・仕上げ実装 編〜

      UnrealEngineC++制作日記ミニゲームUE5
      2024-05-18

      マイケル
      マイケル
      みなさんこんにちは! マイケルです!
      エレキベア
      エレキベア
      こんにちクマ〜〜〜
      マイケル
      マイケル
      「ミニゲームの制作過程を通してUnrealC++で開発する際の方法をざっくり知ろう」という趣旨で、全三回に分けてUnrealEngineでのミニゲーム制作について紹介しています。 前回はキャラクター制御とゲーム内容の実装について紹介したので、今回は仕上げとして味付けを行います!
      【UE5】第一回 ミニゲーム制作で学ぶUnrealC++ 〜UnrealC++の概要 編〜
      2024-05-18
      【UE5】第二回 ミニゲーム制作で学ぶUnrealC++ 〜キャラクター・ゲーム実装 編〜
      2024-05-18

      ▲前回の記事


      ▲制作したゲーム
      エレキベア
      エレキベア
      猫のランゲームについて制作したのだったクマね
      マイケル
      マイケル
      今回は仕上げとして ・UIの実装 ・タイトル画面の作成とレベル遷移 ・サウンド、エフェクト実装 について紹介していきます。 コードは下記リポジトリにあげていますので、こちらもよければ合わせてご参照ください!

      ▼GitHubリポジトリ
      GitHub - plasmo310 / ue5-cat-runner

      ▼UnrealEngineバージョン
      5.3.2

      エレキベア
      エレキベア
      やったるクマ〜〜〜

      参考書籍

      マイケル
      マイケル
      今回実装・情報整理するにあたり、下記書籍を参考にさせていただきました!

      C++でつくるUnreal Engineアプリ開発 for Windows & ...

      Unreal Engine 5で極めるゲーム開発:サンプルデータと動画で学ぶゲー...

      マイケル
      マイケル
      C++でつくるUnreal Engineアプリ開発は、UE4の情報にはなりますが数少ないC++について触れられた書籍です。 Unreal Engine 5で極めるゲーム開発は通称極め本と呼ばれていて、業界内では定番書籍として扱われています。 量はありますが、一度見ておいて損はないと思います!
      エレキベア
      エレキベア
      極め本はUE4版もあったクマが、UE5になってまたボリュームが大きくなったクマね

      UI制御の実装

      マイケル
      マイケル
      まずはUIの実装から見ていきます。 UE5ではUMG (Unreal Motion Graphics) という機能を使用して実装します。

      UMGの概要について

      マイケル
      マイケル
      UMGはUnityでいうところのCanvas的なイメージで、Widgetという単位で作成します。 Widgetの中に更にWidgetを入れ子構造で持たせることもできるので、制御しやすい単位でWidgetを分けるのが重要になります。

      参考:
      UnrealEngineドキュメント - UMG UI デザイナのクイック スタート ガイド
      UnrealEngineドキュメント - UMG UI デザイナ ユーザーガイド

      20240519_03_ue5_cpp_ui_effect_01
      ▲UnityでいうCanvas的なイメージ

      20240519_03_ue5_cpp_ui_effect_03
      ▲Widget内にWidgetを入れ子にできる

      マイケル
      マイケル
      ちなみに今回は下記のような単位で作成しています。 Widgetとして分けておくと、ある程度まとめての表示/非表示切り替えの制御も行いやすいです。
      Widgetの分割単位
      Widget名
      役割
      RunGameWidget
      以下のWidgetを含む
      RunGameScoreInfoWidget
      スコア情報
      RunGameMessageInfoWidget
      メッセージ表示
      RunGameLevelUpWidget
      レベルアップ表示
      RunGameResultWidget
      リザルト表示
      エレキベア
      エレキベア
      UMGではWidgetの単位で考えるクマね
      マイケル
      マイケル
      あとはUMG画面内でアニメーションが作成できたり、Imageの角を丸められたりなどといった機能も付いているのが便利です。
      20240519_03_ue5_cpp_ui_effect_02
      ▲UMG内にアニメーションタブもある

      20240519_03_ue5_cpp_ui_effect_04
      ▲ProceduralUIのような機能も付いている

      エレキベア
      エレキベア
      角を丸められるのはUnityだと有料アセットでしか見ないクマからこれはありがたいクマね
      【Unity】「Procedural UI Image」を使ってシンプルなUIを爆速で作成する
      2022-08-22

      ▲UnityではProcedural UI Image という有料アセットで似たようなことができる

      マイケル
      マイケル
      予想ではあるけど多分Unrealの機能であったからUnityでも使えるようにしたのかな?と思っています。 Unityでも標準機能にしてほしい・・・。

      UI表示の動的操作について

      マイケル
      マイケル
      そしてコードからUIをどう操作するのか?についてですが、まずUUSerWidgetクラスを継承したクラスを親クラスに指定してWidgetを作成する必要があります。
      /**
       * RunGameWidget: リザルトUI
       */
      UCLASS()
      class URunGameResultWidget : public UUserWidget
      {
      	GENERATED_BODY()
      
      protected:
      	virtual void NativeConstruct() override;
      	virtual void NativeOnInitialized() override;
      
      ・・・
      
      ▲UUSerWidgetを継承して作成する
      20240519_03_ue5_cpp_ui_effect_05
      ▲Widget作成時に親クラスとして指定する

      マイケル
      マイケル
      そして個々のUI要素を紐づける方法はいくつかありますが、今回はUPROPERTYにメタ属性として「BindWidget」「BindWidgetAnim」を指定することで紐づける方法を使用しています。 こうすることで、コンパイル時に変数名と同名のUI要素を紐づけてくれます。
      protected:
      	/** リザルトスコアテキスト */
      	UPROPERTY(meta = (BindWidget))
      	TObjectPtr<UTextBlock> ResultScoreText;
      
      	/** リザルト 出現アニメーション */
      	UPROPERTY(Transient, meta = (BindWidgetAnim))
      	TObjectPtr<UWidgetAnimation> ResultAppearAnimation;
      
      ▲UPROPERTYでBindWidgetを設定することで紐づける
      エレキベア
      エレキベア
      自動バインディング的な機能があるのクマね
      マイケル
      マイケル
      ちなみにコンパイル時にエラーにしたくない場合には「BindWidgetOptional」など後ろに「Optional」と付けることで回避できます。 またコードを使用しないプロパティバインディングについては、下記の公式ドキュメントをご参照ください。

      参考:
      UnrealEngineドキュメント - プロパティのバインディング

      エレキベア
      エレキベア
      絶対にUIと紐づけられてるのを保証するのであれば、Optionalは付けない方がよさそうクマね

      その他Tips

      まとめて操作したい場合にはBorder等でまとめる
      マイケル
      マイケル
      アニメーション等で複数のUI要素を一括で操作したい場合、Border等でまとめると解決する場合があります。
      20240519_03_ue5_cpp_ui_effect_06
      ▲一括で操作したい場合、Border等でまとめる

      エレキベア
      エレキベア
      ちょっと面倒くさいクマね・・・
      画面解像度に合わせてUI要素をスケーリングする設定
      マイケル
      マイケル
      また、Unityのように画面解像度に合わせてUI要素をスケーリングしたい場合、 caleBox、SizeBox、CanvasPanelの順で格納するといい感じに調整できました。
      20240519_03_ue5_cpp_ui_effect_07
      ▲画面解像度に合わせてスケーリングする場合

      参考:
      ウィジェットの大きさを画面に合わせる その2 - 妹でもわかるUnrealEngine4

      エレキベア
      エレキベア
      これで画面解像度が変わっても比率を守って調整してくれるクマね
      マイケル
      マイケル
      お手軽で調整できますが、パフォーマンス面を考慮する場合には他の方法を検討する必要もあるかもしれません。

      レベル遷移の実装

      マイケル
      マイケル
      次にレベル遷移処理を把握しておきたかったこともあり、簡易的なタイトル画面を作成してみました。 こちらを使用してレベル遷移を実装してみます。
      20240519_03_ue5_cpp_ui_effect_08
      ▲とりあえず作成したタイトル画面

      レベル遷移処理
      マイケル
      マイケル
      とはいえレベル遷移させるの自体は簡単で、用意されているUGameplayStatics::OpenLevel関数を呼び出すだけで遷移することができます。
      	TitleLevelUiData->SetOnPushStartButtonEvent([this]()
      	{
      		// RunGameレベルへ遷移する
      		UGameplayStatics::OpenLevel(GetWorld(), "RunGame");;
      	});
      
      ▲レベル遷移処理
      エレキベア
      エレキベア
      まあこんなもんクマね
      GameInstanceを使用したデータ保持
      マイケル
      マイケル
      課題となるのはこのレベル間でデータを共有するのをどうするか?になります。 こちらはレベル遷移しても保持されるGameInstanceクラスを生成してプロパティを保持することで対処しました。

      参考:
      Qiita - UE4 GameInstance、Singletonへc++からアクセスする
      UnrealEngineドキュメント - ゲーム フローの概要

      // Copyright (c) 2024, CatRunner All Rights Reserved.
      
      #pragma once
      
      #include "CoreMinimal.h"
      #include "Audio/AudioService.h"
      #include "Engine/GameInstance.h"
      #include "CatRunnerGameInstance.generated.h"
      
      /**
       * CatRunnerGameInstance
       * シーンをまたいで永続的に扱うオブジェクトを定義する
       */
      UCLASS()
      class CATRUNNER_API UCatRunnerGameInstance : public UGameInstance
      {
      	GENERATED_BODY()
      
      	UCatRunnerGameInstance(const FObjectInitializer& ObjectInitializer);
      
      public:
      	static UCatRunnerGameInstance* GetInstance();
      	static TObjectPtr<UAudioService> GetAudioService() { return GetInstance()->AudioService; }
      
      private:
      	UPROPERTY()
      	TObjectPtr<UAudioService> AudioService;
      
      };
      
      
      // Copyright (c) 2024, CatRunner All Rights Reserved.
      
      
      #include "GameInstance/CatRunnerGameInstance.h"
      
      #include "Audio/AudioService.h"
      
      UCatRunnerGameInstance::UCatRunnerGameInstance(const FObjectInitializer& ObjectInitializer)
      	:AudioService(nullptr)
      {
      	AudioService = NewObject<UAudioService>();
      }
      
      UCatRunnerGameInstance* UCatRunnerGameInstance::GetInstance()
      {
      	UCatRunnerGameInstance* Instance = nullptr;
      
      	if (GEngine != nullptr)
      	{
      		const FWorldContext* CurrentWorldContext = GEngine->GetWorldContextFromGameViewport(GEngine->GameViewport);
      		Instance = Cast<UCatRunnerGameInstance>(CurrentWorldContext->World()->GetGameInstance());
      	}
      	return Instance;
      }
      
      マイケル
      マイケル
      今回の規模だと基本的に保持するデータはないのですが、オーディオ再生を管理するクラス(AudioService)のみ保持することにしました。 注意点として、UPROPERTYを付与しておかないとGC対象となってしまうことがあることです。 自分はこれのせいでMacだけデータが保持されずしばらく調査に悩むといった事象にあたりました...。
      エレキベア
      エレキベア
      UPROPERTYの付け忘れは恐ろしいクマ・・・

      サウンド・エフェクトの実装

      マイケル
      マイケル
      最後に味付けでサウンドとエフェクトを実装します。

      サウンド再生の実装

      マイケル
      マイケル
      サウンド再生もそこまで難しいことはなく、音源をUSoundBaseクラスとして取得し、UGameplayStatics::PlaySoundXXX関数を使用することで再生できます。
      void UAudioService::PlaySe2d(USoundBase* PlaySe)
      {
      	if (PlaySe == nullptr)
      	{
      		return;
      	}
      	const UWorld* CurrentWorld = GEngine->GetWorldContextFromGameViewport(GEngine->GameViewport)->World();
      	UGameplayStatics::PlaySound2D(CurrentWorld, PlaySe);
      }
      
      void UAudioService::PlaySe3d(USoundBase* PlaySe, const FVector& PlayLocation)
      {
      	if (PlaySe == nullptr)
      	{
      		return;
      	}
      	const UWorld* CurrentWorld = GEngine->GetWorldContextFromGameViewport(GEngine->GameViewport)->World();
      	UGameplayStatics::PlaySoundAtLocation(CurrentWorld, PlaySe, PlayLocation);
      }
      
      ▲基本の2D/3D再生
      エレキベア
      エレキベア
      位置も渡すことで3D再生できるのクマね
      マイケル
      マイケル
      もう一つの方法として、UAudioComponentを作成して操作する方法があります。 こちらはUnityでいうAudioSource的なもので、再生・停止といった操作も柔軟に行うことができます。
      	/** BGM再生用AudioComponent */
      	UPROPERTY()
      	TObjectPtr<UAudioComponent> BgmAudioComponent;
      
      ▲AudioComponentの定義
      void UAudioService::PlayBgm(USoundBase* PlayBgm)
      {
      	if (PlayBgm == nullptr)
      	{
      		return;
      	}
      
      	// 初回orシーン遷移後はAudioComponentを生成する
      	if (BgmAudioComponent == nullptr || !IsValid(BgmAudioComponent))
      	{
      		const UWorld* CurrentWorld = GEngine->GetWorldContextFromGameViewport(GEngine->GameViewport)->World();
      		BgmAudioComponent = UGameplayStatics::SpawnSound2D(CurrentWorld, PlayBgm);
      		return;
      	}
      
      	// 再生する音源を差し替える
      	BgmAudioComponent->Stop();
      	BgmAudioComponent->SetSound(PlayBgm);
      	BgmAudioComponent->Play();
      }
      
      void UAudioService::StopBgm() const
      {
      	if (BgmAudioComponent == nullptr)
      	{
      		return;
      	}
      	BgmAudioComponent->Stop();
      }
      
      ▲AudioComponentによる再生
      マイケル
      マイケル
      今回はこのような簡潔な処理をAudioServiceクラスとして抜き出して作成しています。 ボリューム調整など、サウンド周りはもう少し作り込む余地がありそうですね。
      エレキベア
      エレキベア
      Unityで頑張って実装したオーディオクラスは下記を参照してくれクマ〜〜
      【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
      2024-03-27

      エフェクト再生の実装

      マイケル
      マイケル
      エフェクト再生については、Niagaraという機能を使用して作成しています。 こちらはUnityでいうShurikenのようなもので、高機能なパーティクルシステムとなっています。

      UnrealEngineドキュメント - Niagara のクイックスタートガイド

      エレキベア
      エレキベア
      Niagaraという名前はよく聞くクマ
      マイケル
      マイケル
      GPUSimulationという機能を使用してComputeShaderの代わりにも使えたりと、中々奥が深い機能なんだ。 こちらも別途別の記事で紹介しようかなと思っています!
      Niagaraの構成要素
      エフェクトの構成
      マイケル
      マイケル
      Niagaraは大きく「システム」「エミッタ」で構成されています。 システムはレベルに配置できる単位で、その中に放出源であるエミッタを複数持たせることができます。
      20240519_03_ue5_cpp_ui_effect_15
      • システム
        • レベルに配置することのできる単位
        • エミッタを複数持つことができる
      • エミッタ
        • パーティクルの放出源
        • 他のシステムからも使用することができる
      エミッタの構成
      マイケル
      マイケル
      そしてエミッタはモジュールという機能を持たせることで実装します。 数や放出する形状、速度などを設定した後、レンダラーにスプライトやメッシュ等を指定することで描画することができます。
      20240519_03_ue5_cpp_ui_effect_16
      • モジュール
        • 上から下に向かうように1つずつ順番に実行され、更新モジュールは毎フレーム繰り返し実行される
        • エミッタのスポーン、更新など、ラックの一つ一つがモジュールに該当する
      • レンダラー
        • 処理した粒子の位置等の情報を拾い、実際に画面に表示させる
        • スプライト、メッシュなどの種類がある
      エレキベア
      エレキベア
      大体イメージは分かったクマ モジュールについてはまたいろいろ勉強する必要はありそうクマね
      星のエフェクト
      マイケル
      マイケル
      今回は星のテクスチャを使ったエフェクトを複数用意していて、打ち出す数と形状を変えることで印象を変化させています。
      20240519_03_ue5_cpp_ui_effect_09
      ▲アイテム取得時などのエフェクト

      20240519_01_ue5_cpp_run_game_09
      20240519_03_ue5_cpp_ui_effect_10
      ▲一つを真上に打ち出す

      20240519_03_ue5_cpp_ui_effect_11
      ▲三つを真上にCone状で打ち出す

      20240519_03_ue5_cpp_ui_effect_12
      ▲多量を球状に打ち出す

      エレキベア
      エレキベア
      同じテクスチャでもモジュールを変えることでいろんなエフェクトが作れるのクマね
      煙のエフェクト
      マイケル
      マイケル
      そしてプレイヤー死亡時の煙(爆発?)は、下記のようなもわっとしたテクスチャを用意して球状に打ち出すことで表現しました。
      20240519_01_ue5_cpp_run_game_10
      20240519_03_ue5_cpp_ui_effect_14
      ▲煙を球状に打ち出す

      エレキベア
      エレキベア
      簡単なエフェクトクマね
      マイケル
      マイケル
      作成したエフェクトは以上になります! 今回実装したのは本当に簡単なものだけど、作り込めば商用レベルのエフェクトを作ることも可能なので、興味がある方はいろいろ試してみてください!

      おわりに

      マイケル
      マイケル
      以上でゲームは完成です! 三回分に分けて紹介してみましたが、どうだったかな?
      エレキベア
      エレキベア
      やっぱゲーム制作は楽しいクマ〜〜〜 こういう小規模なゲームでも一通り作ると開発の感触が掴みやすいクマね
      マイケル
      マイケル
      UnrealEngineだとどうしても大規模なゲーム開発がメインになるから、ミニゲーム規模で理解しやすいプロジェクトが中々ないんだよね・・・ 今回の記事が誰かの役に立てば幸いです!
      エレキベア
      エレキベア
      まあ個人開発規模のゲームなら正直Unityの方が向いてそうクマからね・・・
      マイケル
      マイケル
      今後はもちろんUnityも触っていきますが、UnrealEngine関連の記事も増えるかもしれないのでお楽しみに! アデューー!!
      エレキベア
      エレキベア
      おつかれクマ〜〜〜〜

      【UE5】第三回 ミニゲーム制作で学ぶUnrealC++ 〜UI・仕上げ実装 編〜 〜完〜

      【UE5】第一回 ミニゲーム制作で学ぶUnrealC++ 〜UnrealC++の概要 編〜
      2024-05-18
      【UE5】第二回 ミニゲーム制作で学ぶUnrealC++ 〜キャラクター・ゲーム実装 編〜
      2024-05-18
      【UE5】第三回 ミニゲーム制作で学ぶUnrealC++ 〜UI・仕上げ実装 編〜
      2024-05-18

      UnrealEngineC++制作日記ミニゲームUE5
      2024-05-18

      関連記事
      【UE5.5】Nanite、Lumen、VSMの概要についてまとめる
      2025-05-12
      【UE5】Niagara SimulationStageによるシミュレーション環境構築
      2024-05-30
      【UE5】第二回 ミニゲーム制作で学ぶUnrealC++ 〜キャラクター・ゲーム実装 編〜
      2024-05-18
      【UE5】第一回 ミニゲーム制作で学ぶUnrealC++ 〜UnrealC++の概要 編〜
      2024-05-18
      【JUCE】DTMプラグインを作ってみる 〜ディストーション編〜【VST/AU】
      2024-03-22
      【Unity】「怪盗チョコレート」をリリース!工夫点や反省点をざっと振り返る【バレンタイン】
      2023-02-12
      【DirextX12】第四回 DirextX12を使ったゲーム開発 〜FBX SDKを使用した3Dモデル描画〜
      2022-12-07
      【DirextX12】第三回 DirextX12を使ったゲーム開発 〜座標変換と3Dオブジェクト表示〜
      2022-11-27