PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

UE5 Keyboard / Gamepadの入力種別を判定するには

環境

  • Unreal Engine 5.8.1

概要

Common UI Pluginを使用することで、現在の入力タイプがキーボードかゲームパッドかを取得することが可能です。

UE4の頃はAnyKeyからIsKeyboardKeyやIsGamepadKeyを使用することが多かったかと思いますが
入力の度に処理が走ってしまって大変非効率でした。

UE4時代の実装


AnyKeyの入力をどこで受け取るか(GameMode, Level Blueprint, PlayerController...etc)
といった悩みもありました。
基本的にはUE5でもこの実装方法でも問題なく動作しますが
パフォーマンスや設計の観点からあまり良い実装とは言えません。

Comon UI Pluginでの実装(UE5)

一方でUE5ではOnInputMethodChangedを使用することで判定が可能です。

こちらはAnyKeyとは異なり
Mouse / Keyboardに切り替わった瞬間、Gamepadに切り替わった瞬間のみ処理が走ります。

また、現在の入力種別がMouse and Keyboardか、Gamepadかどうかを判定したい場合には
GetCurrentInputTypeノードで識別することが可能です。

まとめ

UE5以降は基本的にCommon UI Pluginを使う方式で問題がないかと思います。
あとは、OnInputMethodChangedにUI側の処理もバインドすれば
キーボード操作の時のUI、ゲームパッド操作の時のUIといった表示切り替えも容易に行なえて便利です。

UE5 バックアップの復元について

環境

  • Unreal Engine 5.8.1
  • Windows 10 Pro | 11 Pro

概要

VCS(GitやSVNやPerforce等のバージョン管理)を使用していても、それとは別にデータを復元したいケースというのがあります。
基本的にはVCSでrevertすればいいのですが、ローカルの作業データを上書きしてしまった場合などについての話になります。

UE5のコンテンツの復元

UE5はバックアップをある程度用意してくれています。
例えば、コンテンツ直下のデータ(BlueprintやLevel...etc)の場合
プロジェクトの Saved/Autosaves/Game/以下に残っていることがあります。

例としてContent/Blueprints/BP_MyPlayerを復元したい場合

Saved/Autosaves/Game/Blueprints/BP_MyPlayer_Auto1.uasset
Saved/Autosaves/Game/Blueprints/BP_MyPlayer_Auto2.uasset
...
Saved/Autosaves/Game/Blueprints/BP_MyPlayer_Auto7.uasset

といったように残っているケースがあります。

あとは、復元したいデータを元のフォルダにコピーしてファイル名を
BP_MyPlayer_Auto7.uasset -> BP_MyPlayer.uasset のように変更すれば更新が出来ます。

Plugins以下のデータの場合も同様に
Saved/Autosaves/{PluginName}/以下にあります。

Windowsでのデータの復元について

これはUE5に限らず、他のゲームエンジンでもDCCツールでも共通になります。
なんだか初めてのパソコン教室みたいな内容ですが
Windowsもバックアップ(File History)を用意してくれています。
特定のフォルダ/ファイルのプロパティを開いて
以前のバージョンを選択すればある程度古いデータを復元することが出来ます。

稀に、アーティストさんから「DCCツールのローカルデータを誤って消しちゃったから作り直した!」という話を聞くことがありますが
一旦落ち着いて復元可能なデータがないか探してもらうと作業が無駄にならずに済むと思います。

まとめ

UE5のコンテンツはSaved/Autosaves/以下にある程度残っていることがある。
Windowsのフォルダ/ファイルの場合はプロパティから以前のバージョンを選択
コミット前のローカルデータが吹き飛ぶと焦りますが、意外となんとかなることがあります。

UE5 ON_SCOPE_EXITの話

この記事はUnreal Engine (UE) - Qiita Advent Calendar 2025 - Qiitaシリーズ 4の20日目の記事となります。

環境

概要

ON_SCOPE_EXITはスコープを抜けるときに必ず実行したい処理をラムダで簡潔に書ける便利マクロです。

利用例としては以下のケースが考えられます。
・早期returnが多い関数での失敗時処理の一元化
・リソース解放や状態復元の漏れ防止
 ファイルクローズ、ロック解放、フラグ復元、一時的に変更した内部状態のロールバック等
・成功時のみキャンセルする形でのガード的なケース

具体的な利用例

例としてActorからHTTP Requestを投げてCallbackを受け取った際の処理を考えます。
・途中で失敗する可能性が複数ある
・失敗時には必ずDelegateを呼びたい
・成功した場合のみDelegate呼び出しを抑制したい
という、早期returnが増えがちなケースです。
gist.github.com

ON_SCOPE_EXITを使用した場合

以下はON_SCOPE_EXITを使った実装例です。
関数冒頭で失敗時に呼びたい処理を登録しています。
処理が最後まで正常に進んだ場合のみ失敗時の処理をキャンセルする構造になっています。
gist.github.com
このように書くことで、エラー時の早期return箇所が複数あっても
必ずDelegateが呼ばれることが保証される構造になっています。

ON_SCOPE_EXITを使用しない場合

実際にはこんなコードを書くことはまずありえないですが
早期returnが正義という思想で無理やり書いたコードになります。
gist.github.com
このように処理に失敗して早期returnを行いたいが
その時に登録したDelegateも実行したいという場合にON_SCOPE_EXITを使うとスッキリ出来ます。

まとめ

ON_SCOPE_EXITはスコープ終了時に実行したい処理を書くためのマクロです。
・早期returnが多い関数で効果が発揮できる
・失敗時処理 / 後処理 / 状態復元を一元管理できる
・成功時のみキャンセルするガードとして使える点も便利

UEではHTTP、非同期処理、リソース管理、フラグ制御などなど
ON_SCOPE_EXITを使えばコードの質を改善できるケースがあります。
実際にエンジンコード内でも解放処理が漏れないように使用されているケース等があります。
エンジンコードを見てより具体的な使用イメージを掴んでいただければと思います。

UE5 UE_LOGをもうちょっと使いやすくする話

環境

概要

実際に記述方法として
先日登壇したUnreal Engine Meetup Connect - Vol.1 - ゲーム開発編にて紹介させていただきました。
Unreal Engine Meetup Connect - Vol.1 - ゲーム開発編 - connpass

本スライドのp12 - 15の内容をもう少し分かりやすくまとめた記事になります。
イスを支える技術 | ドクセル

ソースコードの全文は最後のまとめに貼ってあります。
コピペしていい感じに使ってください。

通常のUE_LOGのおさらい

以下のように記述するケースが多いかと思います。

UE_LOG(LogTemp, Log, TEXT("Log Message."));
UE_LOG(LogTemp, Warning, TEXT("Log Warning."));
UE_LOG(LogTemp, Error, TEXT("Log Error."));

これでもよいですが、LogTempはEngineやPlugin内でも100箇所近くで使用されており、何かと混在する可能性があります。
そのため、LogCateogoryを追加するのが望ましいです。

UE_LOGをもうちょっと使いやすくするには

今回のProject構成
プロジェクト/モジュール名 : Odin
プロジェクト構成

+- Source
  +- Odin (Module名)
    +- Odin.h
    +- Odin.cpp
    +- Odin.Build.cs
+- Odin.uproject

ここにOdinLoggerクラスを追加しました。

プロジェクト構成

+- Source
  +- Odin (Module名)
    +- Core
        +- OdinLogger.h
        +- OdinLogger.cpp
    +- Odin.h
    +- Odin.cpp
    +- Odin.Build.cs
+- Odin.uproject

LogCategoryの追加

LogCategoryを追加するには
以下の2つのMacroを追加してやる必要があります。

DECLARE_LOG_CATEGORY_EXTERN({"LogCategoryName"}, Log, All);
DEFINE_LOG_CATEGORY({"LogCategoryName"});

それぞれ、headerとcppに定義を追加します。

あとはUE_LOGを使用したいクラス箇所でOdinLogger.hをincludeして
以下のように記述することで独自のLogCategoryの使用が可能になります。

#include "Core/OdinLogger.h"

... (省略)
UE_LOG(LogOdin, Log, TEXT("Odin Log Message."));

LogCategory を追加することの利点として
Output Log内のFilterが使えるようになることも利点の1つです。

UE_LOGを独自Categoryで再定義する

さて、先程の実装で新規にCategory分けができて便利になりました。
もう少し利便性を上げたいと思います。

都度UE_LOGのMacroを記述するのは結構手間ですので、以下の書き方だけで実行できるようにします。

ODIN_LOG(TEXT("Odin Log Message"));

先程追加したlog用のheaderファイルを以下のように改善します。

これでUE_LOGの呼び出しと検索がかなり楽になりました。
特にエラーだけやWarningだけを検索したいといったケースで役立つかと思います。
UE_LOGの場合だとEngine側のコードも検索にヒットしてしまうため、別途独自のLoggerを再定義するのはほぼほぼ必須だと思います。

ODIN_LOG(TEXT("Odin Log Message"));
ODIN_WARNING(TEXT("Odin Log Warning"));
ODIN_ERROR(TEXT("Odin Log Error."));

別Moduleでも使用したい場合

ここまでの実装では別Moduleからの呼び出しができません。
DECLARE_LOG_CATEGORY_EXTERNに少し手を加えるだけで対応ができます。
PrefixにModule名_APIを追加してやるだけです。

ODIN_API DECLARE_LOG_CATEGORY_EXTERN(LogOdin, Log, All);

まとめ

・独自のLogCateogoryを追加しよう
・UE_LOGの呼び出しを簡略化しよう
といった内容になります。
単純にUE_LOGの長い記述が短くなるだけでも楽になるので、こういった実装をするのも良いかなと思います。

最後にLoggerのheaderとcppの全文を載せておきます。

以上です!

UE5 大雑把にフリーカメラを使う方法

環境

  • Windows 10 Pro
  • Unreal Engine 5.3.2
  • VRM4U : ver:20240212 (VRM4U_5_3_20240212)
  • VRoid SDK : ver:0.3.2 (VRoidSDK-ue-0.3.2)

概要

昨今のゲームではオープンワールドやアクションゲームやレースゲーム等
様々なジャンルのゲームでフリーカメラ / 撮影モード機能が実装されています。

実際にゲームで実装しようとすると結構な工数がかかって手間ですね。
今回は正確には何も実装していませんが、UEの機能のご紹介を。

フリーカメラを使うには

本題ですが、フリーカメラ機能を使うには
UE5のコンソールコマンドで以下を入力します。

ToggleDebugCamera


英語環境なら「`キー」
日本語環境なら「@キー」
もしくはOutputLogやCmdの欄から入力が出来ます。

サンプルとしてThirdPersonMapでVRMを扱うLevelを用意しました。

こちらのモデルをサンプルにお借りさせていただきました。
黒棘 - VRoid Hub


この状態でToggleDebugCameraを実行すると以下のような画面に切り替わります。

カメラの移動は基本のEditor操作と同じです。
Backspaceキーを押下で画面左に表示されているUIの表示 / 非表示切り替えが可能です。

特筆すべきは、Fキーを押下でFreeze Renderingが使用可能なこと
Bキーを押下で各種Bufferを確認することも可能です。

他にもVキーを押下で各種View Modeの切り替えが可能です。(Reflection Overrideを表示中)

モードは以下の順で切り替わります。
Lit -> Unlit -> Wireframe -> Detail Lighting -> Reflection -> Collison Pawn -> Collision Vis -> Lit ...

操作の一部まとめ
・UI非表示 : Backspace
・Freeze Rendering : F
・Buffer 表示 : B
・View Mode 切り替え : V

まとめ

以上で大雑把にフリーカメラっぽい機能(DebugCamera)を使用できます。
実際にゲームに実装としてのせることは出来ませんが
(ConsoleコマンドはShipping Buildでは動作しないこと、プレイヤーに見せたくないゲーム要素も見えてしまうこと等)
開発中の疑似的なフリーカメラとしては代用が可能かと思います。

また、開発用途として何かしらのキーで切り替えれるようにしておくと楽かもですね。
以下はThirdPersonMapのLevelBlueprintでBackspaceキー押下で切り替えれるようにしたもの。


VRMが気軽に使えるの楽しい!ダイレクトマーケティング!

VRoid SDK for Unreal Engineをみんな使ってみてね!
VRoid SDK - 好きな3Dモデルで遊べる世界を作ろう
VRM4U
セットアップ - VRM4U

UE5 KawaiiPhysicsをBlueprintプロジェクトでもpkg化する方法

環境

  • UE5.3.2
  • KawaiiPhysics v1.14.0
  • Windows 10 Pro

概要

KawaiiPhysicsとは
github.com

おかずさん(https://twitter.com/pafuhana1213)が開発されているPluginで
揺れものを「かんたんに」「かわいく」揺らすことができる優れものです!

本Pluginの使用にあたってpkgを作成する場合、通常はC++プロジェクトへの変換が必要となります。

今回はこのPluginをBlueprintのみのプロジェクトでも(ちょっと強引に)pkg作成できるようにしていきます。
ざっくりと概要を書くと、Blueprintプロジェクトでも強制的に再ビルドさせればOKということです。
※動作保証は出来かねますので詰まりそうだと感じた場合は素直にC++プロジェクトへの変換がオススメです。

前準備

まずは新規にBlueprintプロジェクトを作成します。

続いてDLしてきたPluginをプロジェクトに入れて、プロジェクトを起動
KawaiiPhysicsが有効になっていることを確認します。

これで準備が整いました。

この段階でpkg作成,実行 / Quick Launchを行うと以下のエラーが出力されます。

Plugin 'KawaiiPhysics' failed to load because module 'KawaiiPhysics' could not be found.  Please ensure the plugin is properly installed, otherwise consider disabling the plugin for this project.

実行しようとしたけど、KawaiiPhysicsのmoduleが見つからないよ!とのこと。
どうして・・・

エラー解決、pkg作成までとQuick Launch手順

Web Browser Pluginを有効にします。
※分かりやすいので、Web Browser Pluginを使用していますがデフォルトで無効なPluginであればなんでも良さそうです(要検証)。

あるいは、直接.uproject に以下を追記

{
  ...
  "Plugins": [
    {
      "Name": "ModelingToolsEditorMode",
      "Enabled": true,
      "TargetAllowList": [
        "Editor"
      ]
    },
    // 以下を追記
    {
      "Name": "WebBrowserWidget",
      "Enabled": true
    }
  ]
}

この状態でプロジェクトを再起動して再度Quick Launchを実行すると以下のLogが出力されます。

UAT: Building {Project Name}...

長いLogが流れて、DLL等必要なものが再ビルドされて更新されていることが分かります。

懸念点

UAT: Using Visual Studio 2022 14.38.33133 toolchain ...(省略)

上記のLogが含まれており、ビルドするにあたってC++プロジェクト化は不要ですが
Visual Studio自体のインストールは必要かもしれません。

ここまでの手順でQuick Launch に成功しました。

pkgの作成

Sampleプロジェクトから動作チェックに必要なデータをMigrateしてきました。

Game Default MapをKawaiiPhysicsSample に変更

Development にてpkgを作成

無事にBlueprintのみのプロジェクトでもpkg作成が完了し、pkgを実行することができました。

まとめ

外部Pluginを導入した際にBlueprintのみのプロジェクトではQuick Launchやpkgの作成/実行に失敗することがあります。
今回のように、別途何かしらのPluginを有効化することで強制的に再ビルドさせることができる場合があります。

さて、1つ困ったもので…
このあとにWebWidget Pluginを無効化した場合には再度pkg実行が出来なくなってしまいます。
そのため、出来るだけ影響の少ない小規模なPluginを選定して有効化できると良さそうですね。

ちょっとしたハック的な手法になりますので
少しでも動作がおかしいな?と感じたらC++プロジェクトに変換するのが現状最善の策だとは思います。

他にも外部Pluginの影響で上手く動かないなって時は試してみてください。
因みに、今回はKawaiiPhysicsだったため素直に動作しました。

別のPluginでDependenciesPluginsに色々なPluginが含まれている場合にはもう少し複雑な回避方法になります。
その手順についても、どこかでいずれ公開できればと思います。

以上です!

UE5 ちょっと便利かもしれないプログラミングの書き方

環境

・UE5.3.2
・Rider 2023.3.2

概要

対象読者の想定としてはプロの現役プログラマーというよりは、そこを目指して勉学に励んでいる方のレベルを想定しています。
非プログラマーのアーティストの方にも役立ち部分はあるかもしれません。
現在プログラマーの方には真新しい情報はないと思われます。

UE4/5向けにBlueprintやC++で記述していますが、C#やその他の言語でも活用できるちょっとしたTips程度のものをいくつ書いてみました。
BlueprintとUnreal C++のどちらでも利用可能なものを紹介していきます。

if文 / branchノードの削減または単純化 (Min / Max / Clampの利用)

ここではBlueprintやC++向けに紹介していますが、Min / Max / ClampはMaterialでも使用可能です。
また、0.0 ~ 1.0のClampに関してはMaterialではsaturateノードがあり
個人的にはそちらの方が0 - 1のClampであることが伝わりやすくて好みです。

アイテムや魔法での回復処理を組むケース

まずは以下のようにゲームで回復を行った時に
現在HP + 回復量の値が最大HPを超えたら最大HPとして
そうでない場合は回復後のHPを割り当てるような処理があった場合
通常は以下のように組むことがあると思います。

こういったケースではif文は不要でMinノードを使用することで簡潔に記述できます。

さらに、Blueprintの場合はMathExpressionでより一層簡潔に記述が可能です。

ダメージを受けてHPが減るケース

実際にはAny | Point | Radial Damage Event等を使用しますが、ここでは簡潔に説明するためCustomEventで定義しています。

先程同様にダメージ処理を組んでいきます。
HP - ダメージが0以下ならHPを0とし、そうでない場合はHP - ダメージが現在のHPとなります。

今度はMaxノードを使用します。

Minのケースと同様にMath Expression化可能です。

Clampについて

Clampは上記のMinとMaxを組み合わせたものです。
Value, Min, Maxの3つのPinがあり、ValueがMin以上でMax以下となるように制限をかけてくれます。

最後にC++での記述を載せておきます。

// 実際にはMaxHealth等のパラメーターはDataTableやDataAsset等にして管理するのが望ましいです。
void OnHeal(const float Heal)
{
    Health = FMath::Min(MaxHealth, Health + Heal);
}
void OnDamage(const float Damage)
{
    Health = FMath::Max(0.0f, Health - Damage);
}

配列のwrap around(上限に達したら最初に戻る)

先ずは、以下のように手持ちの武器を次の武器に切り替えるような処理を組んだとします。

このままでも現在選択している武器のindex情報は正しく
剣 -> 銃 -> 拡張武器 -> 剣... といった形で次の武器に切り替えることが可能です。

こういった処理をより簡潔に組むには、index % Max といった形で配列の最大数で剰余してやればよいです。

wrap aroundをよく使う場面としては
ゲームの設定画面やアイテム選択画面なんかが特に多いと思います。
設定内の画面品質のUIで
クオリティー低 -> 中 -> 高 -> 最高 -> 低... といった場面ですね。

C++で組むと以下のようになります。

TArray<FString> WeaponArray{"Sword", "Gun", "ExtraWeapon"};
void ChangeNextWeapon()
{
    CurrentWeanponIndex = (WeaponArray.Num() % ++CurrentWeanponIndex);
}

マイナス方向の配列のwrap around(下限に達したら最大数に戻る)

残念ながらマイナス方向のwrap aroundは剰余で求めることができません。

Xにて親切なお犬様がマイナス方向のwrap aroundについて教えてくださいました。

まずは愚直に組むと以下のような処理になるかと思います。

先程教えていただいた内容を元にノードを組むと以下のようになります。
Blueprintの配列操作にはLastIndexというノードがあり、配列の末尾のindexを取得可能です。
この場合は要素数が3つなので、Lengthは3となり
LastIndexは0, 1, 2と0始まりでカウントされるため末尾の2が取得できます。
内容としてはLength - 1と同義です。

最後にC++で組むと以下のようになります。

void ChangePrevWeapon()
{
    const int WeaponNum = WeaponArray.Num();
    CurrentWeanponIndex = (CurrentWeanponIndex + (WeaponNum - 1)) % WeaponNum;
}


先程のプラス方向のwrap aroundと組み合わせて
プラスマイナスの双方向を考慮したWrap関数を作っておくと便利かもしれません。

小数点の比較 (floatの比較)

例えば以下のように何かしらのfloat値に計算をして、==ノードで0.0 かどうかを比較したいといったケースがあると思います。

この場合に、良くも悪くもBlueprintではわりと動いてtrue になることがあるのですが
基本的に小数点の比較というのはプログラミングでは御八度です。
小数点の誤差でfalse になるケースがほとんどだったりします。

こういった誤差を考慮してだいたい同じ値という比較用のノードが用意されています。
それがNearly Equalノードです。
原則として、このノードを使うようにするとよいでしょう。

C++で記述すると以下です。

bool IsZeroFloat(const float Value)
{
    // C++の場合は0との比較用にIsNearlyZero()があります
    return FMath::IsNearlyZero(Value);
    // return FMath::IsNearlyEqual(Value, 0.0f);
}

余談ですが、UnityではMathf.Approximatelyを使用することで比較が可能です。

早期returnの活用

正直なところ、Blueprintでは特にシンプルになった感じがなくあまり恩恵を感じられないかもですが・・・
攻撃用の関数を以下のように組んだとします。
HPが0以上で攻撃のクールタイムが0になっていたら攻撃をするといった関数です。

条件式として
HPが0以上 かつ
クールタイムが0
であれば攻撃となります。

こういったケースでは早期returnを使用すると良いとされています。
HPが0であれば攻撃しない
クールタイムが残っていれば攻撃しない
といった条件式に置き換えることができます。

Blueprintでは恩恵を受けれている感じがないですね。
C++で記述してみます。
先ずは早期return なしver

void Attack()
{
    if (Health > 0.0f)
    {
        if (FMath::IsNearlyZero(CoolTime))
        {
            // 何かしらの攻撃処理
        }
    }
}

このようにネストが深くなって処理が読みにくいです。
早期returnで書き換えると以下のようにスッキリします。

void Attack()
{
    if (FMath::IsNearlyZero(Health))
        return;
    if (0.0f < CoolTime)
        return;
    // 何かしらの攻撃処理
}

まとめ

C++に絞ればもっとたくさんのネタがありますが、BlueprintとC++で考えてみるとあまり浮かびませんでした。
いずれも学生時代の頃に知って便利だと感じた小ネタになります。

早期return や説明用のlocal 変数定義等については、C++やその他の言語では読みやすくなって便利なのですが
いかんせんノードエディタではその恩恵があまり受けられず
むしろlocal変数を増やしたりすることで余計にノード郡が大きくなってしまったりと逆効果かな?と感じることがあります。

こういった浅いネタでなく、もう少しちゃんとしたネタが浮かべばまた書いていこうと思います。