1. 概要
本記事ではVisual Studio の Immediate Window を利用してUnreal Engineに生かすためのヒントを記載しています。この内容は公式ドキュメント、Visual Studio のヒントとコツの一部を補足するもので、以下のような事ができるようになります。
- エンジンクラスや自作したカスタムクラスで定義された変数の素早いウオッチ、書き換え
- Visual Studioでブレーク中に各クラスのモジュールを実行
これらの操作がVisual Studioで完結するなどによって、コンソール開発においても反復移動を減らすことができます。
2. 使い方
デバッグを行う方法は以下の流れになります。
- プロジェクトを起動してImmediate Window を開く
- 任意の箇所でブレークする
- Immediate Window にコマンドや変数を入力する
1. プロジェクトを起動してImmediate Window を開く
プロジェクトを起動してVisual StudioでImmediate Windowを開きます。プロジェクトはVisual Studioから起動しても、動作中のプロジェクトにVisual Studioからアタッチする方法でもどちらでも良いです。Immediate Windowは、[Debug]->[Windows]->[Immediate] から開くことができます。
2. 任意の箇所でブレークする
Immediate Windowはブレーク中にコマンドを受け付ける事ができるため、任意の箇所にブレークポイントを追加してプロジェクトをブレークした状態にします。
3. Immediate Window にコマンドや変数を入力する
Immediate Windowにコマンドや変数を入力したりします。一例として挙げられる内容としては、公式ドキュメント、Visual Studio のヒントとコツにも記載されている内容が参考になります。こちら2022/06/30 現在ではコマンドの内容がUE4のものとなっているため、UE5で利用する場合には以下のように置き換えてください。
コマンド | 説明 |
---|---|
{,,UnrealEditor-Core}::PrintScriptCallstack() | ブループリント コールスタック |
{,,UnrealEditor-Core}::GFrameNumber | 現在のフレーム数 (ブレークポイント条件としても機能します) |
{,,UnrealEditor-Core}::GPlayInEditorID | PIE ID (マルチプレイヤーの場合に便利であり、ブレークポイント条件としても機能します) |
UnrealEditor-Engine!GPlayInEditorContextString | PIE ウィンドウ名 (マルチプレイヤーの場合に便利です) |
コマンドの入力した例を以下に示します。例えば一番上の「ブループリント コールスタック」を出力したい場合は、コンソール画面に以下の内容を入力します。
{,,UnrealEditor-Core}::PrintScriptCallstack()
そうするとコールバックとして以下のような出力が行われます。これはブレークの過程で実行されたブループリントのコールスタックを示しています。「レベルブループリントからアクター(BP_TestActor)をスポーンした時に、スポーンされた親のC++クラスコンストラクタでブレークを張っている」状況であるためこのようなコールスタックとなっています。
{,,UnrealEditor-Core}::PrintScriptCallstack()
[2022.06.27-03.16.50:579][162]LogOutputDevice: Warning:
Script Stack (4 frames):
BP_TestActor_C.ExecuteUbergraph_BP_TestActor
BP_TestActor_C.SpawnActor
NewMap_C.ExecuteUbergraph_NewMap
NewMap_C.InpActEvt_One_K2Node_InputKeyEvent_0
<void>
他にもコマンドが存在していますが、キーとなっているのが {,,UnrealEditor-Core} の部分で、これは実行ファイル名(UnrealEditor)とモジュール名(Core)から構成されており、これらを応用することで様々なことができるようになります。クラスの前の部分の組み合わせについては、Visual Studioのコールスタックを見ると以下のように呼ばれたモジュールと関数が載っているため、この内容を参考にすることもできます。
UnrealEditor-TP_500.dll!AMyActor::IncrementVal()
UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result)
UnrealEditor-CoreUObject.dll!UObject::CallFunction(FFrame & Stack, void * const Z_Param__Result, UFunction * Function)
その他のコマンドについても実行した内容と出力結果を見てみましょう。このコマンドではブレークしたタイミングでのフレーム番号を出力します。
{,,UnrealEditor-Core}::GFrameNumber
13163
このコマンドではマルチプレイ実行時など複数窓で実行時に割り当てられるIDを出力することができます。どちらのIDで現在実行しているか、どちらのIDでブレークしているかといったことが分かります。以下は2人のマルチプレイ時に各クライアントからコマンドを実行した結果を載せています。
{,,UnrealEditor-Core}::GPlayInEditorID
0
{,,UnrealEditor-Core}::GPlayInEditorID
1
このコマンドではどのクライアントでプレイしているかが分かります。こちらもクライアント3とクライアント1で各コマンドを実行した結果を載せています。
UnrealEditor-Engine!GPlayInEditorContextString
L"Client 3"
Data: Num=9 L"Client 3\0"
UnrealEditor-Engine!GPlayInEditorContextString
L"Client 1"
Data: Num=9 L"Client 1\0"
3. 応用
上記で記載したように固定部分は実行ファイル名とモジュール名の構成になっているため、この部分を変更すれば他のパラメータや関数も実行できることが分かります。ドキュメントに記載されている部分以外の箇所を見てみましょう。
以下では GFrameCounterRenderThread のカウンタは GFrameCounter よりも遅れている事が確認できます。
{,,UnrealEditor-Core}::GFrameCounter
17031
{,,UnrealEditor-Core}::GFrameCounterRenderThread
17030
自分で作成したクラスの変数や内容についても見てみましょう。例えば以下のようなクラスや変数を定義した場合、
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
public:
int32 IntVal = 1;
bool BoolVal;
float FloatVal = 1.0f;
}
void AMyActor::TestFunc()
{
UE_LOG(LogTemp, Warning, TEXT("test"));
}
ブレーク中に以下のように入力すると各変数や関数の実行、値の上書きができます。
UnrealEditor-MyProject!AMyActor::IntVal
1
UnrealEditor-MyProject!AMyActor::BoolVal
false
UnrealEditor-MyProject!AMyActor::FloatVal
1.00000000
UnrealEditor-MyProject!AMyActor::TestFunc()
[2022.XX.XX-XX.XX.XX:XXX][XXX]LogTemp: Warning: test
UnrealEditor-MyProject!AMyActor::intVal=555
555
例えばこれを活かすことによって、以下のようにコンソールコマンド実行用の関数を用意しておくと、
#include "Kismet/KismetSystemLibrary.h"
void AMyActor::ExecConsoleCommand(const char* str)
{
FString TempStr(str);
UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), TempStr, nullptr);
}
ブレーク中に実行することによって、ブレーク解除直後にコンソールコマンドが実行できるようになります。これはブレーク直後にコンソールコマンドを入力したいケースや、画面が無いDedicated Serverにコンソールコマンドをデバッグ実行しながら入力したいようなケースでも役に立ちそうです。
UnrealEditor-MyProject!AMyActor::ExecConsoleCommand("RestartLevel")
[2022.XX.XX-XX.XX.XX:XXX][XXX]Cmd: RestartLevel
これはあくまで1つの例ですが、他にも自作の関数やエンジンの機能にアクセスすることによってデバッグの幅が広がって良いかと思いますので、是非ご活用ください。