mcpField
U
ue-testing-debugging

ue-testing-debugging

github.com/quodsolerテスト・品質4191.3k 回インストール4 週間前に更新
リポジトリ

ue-testing-debugging について

Unreal Engine 5.8 開発者向けのスキルです。ログ、アサーション、自動化テストや機能テスト、コンソールコマンド、パフォーマンス分析を扱います。デバッグ可視化と、ビルド構成ごとの動作も説明します。

主な機能

  1. 01ログカテゴリ、レベル、構造化ログの設定
  2. 02アサーションの選択とビルド別の違い
  3. 03自動化テスト、CQTest、機能テスト
  4. 04コンソールコマンドとコンソール変数の利用
  5. 05パフォーマンス分析とデバッグ可視化

活用シーン

  1. 01UE モジュールの実行情報を記録・確認
  2. 02ゲームロジックの自動テスト作成
  3. 03性能問題の調査や実行状態の可視化

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/quodsoler/unreal-engine-skills --skill ue-testing-debugging

インストール後、Claude Code で/ue-testing-debuggingを入力して読み込まれたか確認します。

同梱ファイルreferences/automation-test-patterns.mdreferences/profiling-commands.md

詳細

UE Testing, Logging & Profiling

対象エンジン:UE 5.8。以下の API は 5.8 のヘッダーに照らして検証済みです。古い形式は「非推奨 — 使用しないでください」に記載しています。

このスキルでは、このリポジトリのログ、アサーション、自動テスト/機能テスト、コンソールコマンド、パフォーマンスプロファイリングを扱います。ログ、アサーション、統計、トレース、CVar は Core(Logging/、Misc/AssertionMacros.h、Stats/Stats.h、ProfilingDebugging/、HAL/IConsoleManager.h)にあります。デバッグ描画、Visual Logger、AHUD::ShowDebug は Engine にあります。テストフレームワークは開発者モジュールの AutomationController、CQTest、FunctionalTesting、AutomationDriver です。Gameplay Debugger はランタイムモジュールの GameplayDebugger です。

コンテキスト

.agents/ue-project-context.md が存在する場合は読み込んでください(モジュール名、規約、有効なプラグイン、GAS/ネットワーク設定)。存在しなくても作業を中断しないでください。依頼とコードベースから対象領域を特定してください。解釈の候補が 2 つあり、それぞれで異なるコードになる場合にのみ質問してください。

依頼の内容…参照先
UE_LOG、カテゴリ、詳細度、構造化ログログ
check、ensure、verify、不正な状態でのクラッシュアサーション
単体テスト、IMPLEMENT_SIMPLE_AUTOMATION_TEST、仕様テスト、潜在コマンド自動テスト
TEST_CLASS、ASSERT_THAT、フィクスチャ形式のテストCQTest
マップに配置するテスト、AFunctionalTest機能テスト
Insights、stat、サイクルカウンター、CSV、メモリ、ヒッチプロファイリング
DrawDebug*、UE_VLOG、Gameplay Debugger、ShowDebugデバッグ可視化
Exec 関数、FAutoConsoleCommand、CVarコンソールコマンドと CVar

ログ

カテゴリの宣言
#include "Logging/LogMacros.h"
// MyGameLogging.h — DECLARE_LOG_CATEGORY_EXTERN(Name, DefaultVerbosity, CompileTimeVerbosity)
DECLARE_LOG_CATEGORY_EXTERN(LogMyGame, Log, All);

DEFINE_LOG_CATEGORY(LogMyGame);   // MyGameLogging.cpp
マクロ場所スコープ
DECLARE_LOG_CATEGORY_EXTERN(Name, Default, CompileTime)ヘッダーモジュールと、そのヘッダーをインクルードするすべての箇所
DEFINE_LOG_CATEGORY(Name)1 つの .cppEXTERN 形式と対になります
DEFINE_LOG_CATEGORY_STATIC(Name, Default, CompileTime)1 つの .cppその翻訳単位のみ
DECLARE_LOG_CATEGORY_CLASS(Name, Default, CompileTime)クラス本体クラスの静的カテゴリ。DEFINE_LOG_CATEGORY_CLASS(Class, Name) と対になります

DefaultVerbosity は <= CompileTimeVerbosity でなければなりません。そうでない場合、カテゴリマクロ内の static_assert が発生します。CompileTimeVerbosity を超えるものはコンパイラーによって削除されます。そのため、DECLARE_LOG_CATEGORY_EXTERN(LogMyGame, Log, Log) はすべての Verbose/VeryVerbose 呼び出し箇所を削除します。モノリシックビルドでは、COMPILED_IN_MINIMUM_VERBOSITY を定義することで、すべてのカテゴリの上限を一度に設定できます。

詳細度レベル
レベルコンソールファイル注記
Fatalはいはいログが無効でもプロセスをクラッシュさせます
Errorはいはいコマンドレットとエディターで収集され、コマンドレットを失敗させます
Warningはいはいエラーに昇格させることができます
Displayはいはいユーザーにその行を必ず見せる必要がある場合に使用します
Logいいえはいファイルのみ。開発メッセージのデフォルトです
Verboseいいえはいカテゴリの詳細度を Verbose に引き上げた場合のみ
VeryVerboseいいえはいカテゴリの詳細度を VeryVerbose に引き上げた場合のみ

ELogVerbosity::All は VeryVerbose の別名で、通常は CompileTimeVerbosity 引数に使用します。

出力
UE_LOG(LogMyGame, Display, TEXT("Player %s spawned"), *PlayerName);
UE_LOG(LogMyGame, Warning, TEXT("Inventory full, dropped %s"), *ItemName);
UE_LOG(LogMyGame, Error,   TEXT("Failed to load asset %s"), *AssetPath);
UE_LOG(LogMyGame, Fatal,   TEXT("Save data unrecoverable"));   // crashes

// Condition and arguments are evaluated only when the category is active.
UE_CLOG(Health <= 0.f, LogMyGame, Warning, TEXT("%s has zero health"), *GetName());

高コストなレポートは if (UE_LOG_ACTIVE(LogMyGame, Verbose)) { … } で囲んでください。書式設定だけでなく、処理そのものをスキップできます。

構造化ログ

UE_LOGFMT は、Insights やログ分析で保持される名前付きフィールドを出力します。%s ではなく、{Name} プレースホルダーを使用してください。

#include "Logging/StructuredLog.h"

// Named fields: order is irrelevant, extra fields are allowed.
UE_LOGFMT(LogMyGame, Warning, "Loading '{Name}' failed with error {Error}",
    ("Name", AssetName), ("Error", ErrorCode), ("Flags", LoadFlags));

// Positional fields: the values must match the placeholders exactly.
UE_LOGFMT(LogMyGame, Display, "Spawned {Count} actors in {Seconds}s", SpawnCount, Elapsed);
UE_CLOGFMT(bFailed, LogMyGame, Error, "Retry {Attempt} failed", ("Attempt", Attempt));

フィールド名は [A-Za-z0-9_]+ に一致し、イベントごとに一意でなければなりません。値は SerializeForLog または operator<<(FCbWriter&, T) を通じてシリアライズされます。UE_LOGFMT が受け取れるフィールドは最大 16 個です。それを超える場合は、UE_LOGFMT_FIELD/UE_LOGFMT_VALUE とともに UE_LOGFMT_EX を使用してください。

Shipping ビルドと Test ビルド

NO_LOGGING は Test と Shipping の両方で !USE_LOGGING_IN_SHIPPING になり、Debug と Development では 0 になります。NO_LOGGING が有効な場合、UE_LOG/UE_CLOG/UE_LOGFMT は Fatal のみの経路に集約され、すべての非致命的な呼び出しは引数も含めて取り除かれます。ログ出力を再び有効にするには、MyGame.Target.cs で bUseLoggingInShipping = true; を設定して、USE_LOGGING_IN_SHIPPING=1 を定義します。

実行時の詳細度の制御
-LogCmds="LogMyGame Verbose, LogNet Warning"   # command line, applied at boot
Log list [Filter]       # every category with its current verbosity
Log LogMyGame Verbose   # raise one category
Log global none         # silence everything
Log reset               # restore defaults

コードでは UE_SET_LOG_VERBOSITY(LogMyGame, Verbose) を使用し、設定では DefaultEngine.ini の [Core.Log] を使用します。

アサーション

すべて Misc/AssertionMacros.h にあります。誰に問題があるかに応じて選択します。プログラマーの誤りには check、報告に値する回復可能な想定外の事態には ensure、ユーザーまたはデータの入力には通常の if を使用します。

check(Component != nullptr);
checkf(Index >= 0 && Index < Items.Num(), TEXT("Index %d out of [0,%d)"), Index, Items.Num());
checkNoEntry();                             // unreachable branch
checkNoReentry();                           // must not be entered twice
checkSlow(ValidateExpensiveInvariant());    // Debug only
verify(Manager->Initialize());              // expression ALWAYS evaluated

if (ensure(Component != nullptr))
{
    Component->Activate();                  // ensure() returns the condition
}
ensureMsgf(Health > 0.f, TEXT("%s has health %.1f"), *GetName(), Health);
ensureAlways(bInitialized);                 // reports every failure, not just the first

ensure/ensureMsgf はセッションごとに呼び出し箇所あたり 1 回だけ 報告し、ensureAlways/ensureAlwaysMsgf は毎回報告します。4 つはいずれも条件を返すため、if と組み合わせて使用できます。

ビルドDO_CHECKDO_ENSUREDO_GUARD_SLOWNO_LOGGING
Debug1110
Development1100
Test および ShippingUSE_CHECKS_IN_SHIPPINGUSE_ENSURES_IN_SHIPPING0!USE_LOGGING_IN_SHIPPING

USE_CHECKS_IN_SHIPPING のデフォルトは 0 で、ターゲットルールの bUseChecksInShipping によって設定されるため、デフォルトでは Test ビルドでも check と ensure が取り除かれます。DO_CHECK が 0 の場合、check(expr) は CA_ASSUME(expr) に変わり、式は評価されません。verify(expr) は引き続き式を評価し、中断のみを無効にします。

自動化テスト

テストのソースは、テスト対象モジュールの Private/Tests フォルダー、または専用のテストモジュールに配置します。各テストファイルを #if WITH_DEV_AUTOMATION_TESTS(または WITH_AUTOMATION_TESTS = WITH_DEV_AUTOMATION_TESTS || WITH_PERF_AUTOMATION_TESTS)で囲みます。IMPLEMENT_* マクロだけではクラス自体はコンパイルされ、WITH_AUTOMATION_WORKER が 0 のときに登録をスキップするだけです(Misc/AutomationTest.h:4366)。UBT は Test と Shipping を除くすべての構成で両方を有効にします。この動作は bForceCompileDevelopmentAutomationTests、bForceCompilePerformanceAutomationTests、bForceDisableAutomationTests で上書きできます(UEBuildTarget.cs:6309-6325)。モジュールとターゲットの設定、パターンの完全なライブラリ、テスト実行用のコマンドラインについては、references/automation-test-patterns.md を参照してください。

// Source/MyGame/Private/Tests/MyInventoryTest.cpp — whole file inside #if WITH_DEV_AUTOMATION_TESTS
#include "Misc/AutomationTest.h"
#include "MyInventoryComponent.h"

IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMyInventoryAddTest, "MyGame.Inventory.AddItem",
    EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter)

bool FMyInventoryAddTest::RunTest(const FString& Parameters)
{
    UMyInventoryComponent* Inv = NewObject<UMyInventoryComponent>(GetTransientPackage());
    UE_RETURN_ON_ERROR(Inv != nullptr, TEXT("Inventory created"));

    Inv->AddItem(FName("Sword"), 1);
    TestEqual(TEXT("Count after add"), Inv->GetItemCount(FName("Sword")), 1);
    TestTrue(TEXT("Has sword"), Inv->HasItem(FName("Sword")));
    TestFalse(TEXT("No axe"), Inv->HasItem(FName("Axe")));
    return true;
}

IMPLEMENT_COMPLEX_AUTOMATION_TEST は virtual void GetTests(TArray<FString>& OutBeautifiedNames, TArray<FString>& OutTestCommands) const を追加し、各エントリーに対して RunTest を 1 回実行します。IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(TClass, TBaseClass, PrettyName, TFlags) は、FAutomationTestBase から派生させた共有フィクスチャを基底クラスとする、シンプルなテストを宣言します。

フラグ

EAutomationTestFlags は ENUM_CLASS_FLAGS を持つ enum class なので、| で組み合わせます。2 つの static_assert により、少なくとも 1 つのコンテキストフラグとちょうど 1 つのフィルターフラグが指定されていることが保証されます。

グループ値
コンテキスト(1 つ以上必須)EditorContext、ClientContext、ServerContext、CommandletContext、ProgramContext
フィルター(ちょうど 1 つ)SmokeFilter、EngineFilter、ProductFilter、PerfFilter、StressFilter、NegativeFilter
優先度CriticalPriority、HighPriority、MediumPriority、LowPriority
その他NonNullRHI、RequiresUser、Disabled、SupportsAutoRTFM

EAutomationTestFlags_ApplicationContextMask は、すべてのコンテキストフラグを 1 つのトークンで網羅します。

テスト内のアサーション
TestEqual(TEXT("Label"), Actual, Expected);              // int32/int64/SIZE_T/FName/FText/FString/FStringView…
TestEqual(TEXT("Approx"), ActualF, ExpectedF, 0.001f);   // float/double/FVector/FRotator/FTransform take a tolerance
TestTrue(TEXT("Label"), bCondition);                     // also TestFalse, TestNotEqual
TestNotNull(TEXT("Label"), Ptr);                         // also TestNull, TestSamePtr, TestNotSamePtr
TestValid(TEXT("Label"), SharedOrWeakPtr);               // also TestInvalid

AddError(FString::Printf(TEXT("Unexpected damage %d"), Damage));   // also AddWarning, AddInfo
AddErrorIfFalse(bOk, TEXT("Setup failed"));
UE_RETURN_ON_ERROR(Ptr != nullptr, TEXT("Ptr must not be null")); // adds error, returns false

// Expect (and swallow) a log line the code under test is supposed to emit.
AddExpectedMessage(TEXT("Inventory is full"), ELogVerbosity::Warning,
                   EAutomationExpectedMessageFlags::Contains);

…Plain バリアント(AddExpectedMessagePlain、AddExpectedErrorPlain)を使わない限り、パターンは正規表現です。AddExpectedError(Pattern, MatchType, Occurrences, bIsRegex) は警告とエラーに一致する簡略形です(ELogVerbosity::Warning を転送するため、これを含む重大度に一致します)。詳細度を指定しない AddExpectedMessage のオーバーロードは、すべての重大度に一致します。

潜在コマンド

RunTest は潜在コマンドの実行前に戻ります。コマンドをキューに追加し、ステップが完了したら Update() が true を返すようにします。return true; の後に追加したコードは到達不能です。

DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FMyWaitSecondsCommand, float, Duration);
bool FMyWaitSecondsCommand::Update()
{
    return GetCurrentRunTime() >= Duration;
}

ADD_LATENT_AUTOMATION_COMMAND(FMyWaitSecondsCommand(2.0f));   // inside RunTest, before `return true;`

DEFINE_LATENT_AUTOMATION_COMMAND から ..._FIVE_PARAMETER までが用意されています。待機後にアサートするには、IAutomationLatentCommand を継承し、FAutomationTestBase* をキャプチャします。

Spec

BEGIN_DEFINE_SPEC(TClass, PrettyName, TFlags)/メンバー宣言/END_DEFINE_SPEC(TClass) は、本文が void TClass::Define() となる BDD スタイルのテストを宣言します。Define() 内では Describe、It、LatentIt、BeforeEach、LatentBeforeEach、AfterEach(およびブロックを無効にする xIt/xDescribe)を使用します。Spec にメンバーが不要な場合は、DEFINE_SPEC(TClass, PrettyName, TFlags) でこの 2 つのマクロを置き換えられます。実例はreferences/automation-test-patterns.mdを参照してください。

CQTest

CQTest(モジュール CQTest)は、同じフレームワークを基盤とするフィクスチャベースのフロントエンドです。Assert は FNoDiscardAsserter なので、すべてのアサーションは ASSERT_THAT を経由し、失敗するとメソッドから戻ります。

#include "CQTest.h"

TEST_CLASS(FMyInventoryTests, "MyGame.Inventory")
{
    UMyInventoryComponent* Inventory = nullptr;

    BEFORE_EACH() { Inventory = NewObject<UMyInventoryComponent>(GetTransientPackage()); }

    TEST_METHOD(AddItem_WhenEmpty_StoresOne)
    {
        Inventory->AddItem(FName("Sword"), 1);
        ASSERT_THAT(AreEqual(1, Inventory->GetItemCount(FName("Sword"))));
    }

    TEST_METHOD_WITH_TAGS(AddItem_WhenFull_Rejects, "[Slow][Inventory]")
    {
        Inventory->SetCapacity(0);
        ASSERT_THAT(IsFalse(Inventory->AddItem(FName("Sword"), 1)));
    }
};

TEST_CLASS(ClassName, TestDir) は FNoDiscardAsserter と DefaultFlags(EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::ProductFilter)を使用します。バリエーションは、TEST_CLASS_WITH_FLAGS(ClassName, TestDir, Flags)、TEST_CLASS_WITH_TAGS、TEST_CLASS_WITH_BASE、TEST_CLASS_WITH_ASSERTS です。ライフサイクルマクロは BEFORE_ALL()/AFTER_ALL()(静的で、フィクスチャごとに 1 回)と BEFORE_EACH()/AFTER_EACH()(Setup()/TearDown() をオーバーライド)です。アサーターのヘルパーは、IsTrue、IsFalse、IsNull、IsNotNull、AreEqual、AreNotEqual、AreEqualIgnoreCase、AreNotEqualIgnoreCase、IsNear(Expected, Actual, Epsilon) です。いずれも末尾に失敗メッセージを指定できます。

ワールド、アクター、非同期処理のヘルパー(TestCommandBuilder、FSpawnHelper、FActorTestSpawner、FMapTestSpawner、TObjectBuilder、UTestGameInstance)については、references/automation-test-patterns.mdを参照してください。

機能テスト

AFunctionalTest(モジュール FunctionalTesting)は、テストマップに配置するアクターです。PrepareTest、StartTest、IsReady_Implementation、OnTimeout は保護された仮想関数で、FinishTest と Assert* ヘルパーはパブリックです。

// MyFunctionalTest.h
#pragma once
#include "CoreMinimal.h"
#include "FunctionalTest.h"
#include "MyFunctionalTest.generated.h"

UCLASS()
class MYGAME_API AMyFunctionalTest : public AFunctionalTest
{
    GENERATED_BODY()

protected:
    // PrepareTest() sets TimeLimit / PreparationTimeLimit; IsReady_Implementation()
    // gates StartTest(); StartTest() asserts and calls FinishTest().
    virtual void PrepareTest() override;
    virtual bool IsReady_Implementation() override;
    virtual void StartTest() override;

private:
    UPROPERTY(EditInstanceOnly, Category = "MyGame")
    TObjectPtr<AActor> TargetActor;
};

各オーバーライドは、最初に Super:: を呼び出します。FinishTest(EFunctionalTestResult TestResult, const FString& Message) は実行を終了します。EFunctionalTestResult の値は Default、Invalid、Error、Running、Failed、Succeeded で、Default を指定すると記録されたアサーションによって結果が決まります。アサーションには、AssertTrue/AssertFalse(bool Condition, const FString& Message, const UObject* ContextObject = nullptr)、AssertIsValid(UObject*, const FString&, const UObject* = nullptr)、AssertEqual_Int/Float/Bool/Name/Object/Vector/Rotator/Transform/Quat、AssertValue_Int/Float/Double/DateTime(EComparisonMethod を指定)があります。また、TimeLimit、PreparationTimeLimit、SetTimeLimit(float, EFunctionalTestResult)、LogMessage(const FString&)、StartStep/FinishStep も使用できます。.cpp 全体はreferences/automation-test-patterns.mdを参照してください。

現在のワールドですべての機能テストを実行するには、Blueprint ノード UFunctionalTestingManager::RunAllFunctionalTests(WorldContextObject, bNewLog, bRunLooped, FailedTestsReproString) を使用するか(クラスは MinimalAPI で、関数は FUNCTIONALTESTING_API ではないため、別モジュールの C++ から呼び出してもリンクされません)、コマンドラインから実行します。

UnrealEditor-Cmd.exe MyGame.uproject -ExecCmds="Automation RunTests MyGame.Functional" -unattended -nopause -testexit="Automation Test Queue Empty" -log

Automation では、List、RunTest、RunFilter <Filter>、RunAll、SetFilter、SetPriority、SetMinimumPriority、Quit、SoftQuit も使用できます。; でつなげて実行します。

プロファイリング

コマンドの全一覧、CSV と LLM のワークフロー、ボトルネックのトリアージ手順については、references/profiling-commands.mdを参照してください。

Unreal Insights
# Capture at launch
UnrealEditor.exe MyGame.uproject -trace=cpu,frame,bookmark,counters,gpu,loadtime,log -tracehost=127.0.0.1
UnrealEditor.exe MyGame.uproject -trace=cpu,frame -tracefile=MyCapture.utrace

# Or from the console, mid-session
Trace.File D:/Captures/Run1.utrace cpu,frame,bookmark    # or Trace.Send 127.0.0.1 cpu,frame
Trace.Enable counters
Trace.Bookmark WaveStarted
Trace.Stop                    # also Trace.Pause, Trace.Resume, Trace.Status

チャンネル名から Channel サフィックスを取り除き、小文字にします:cpu、gpu、frame、bookmark、counters、log、loadtime、task、memalloc、callstack、module、metadata、net、rhicommands、rendercommands、slate、assetmetadata、iostore、animation、region、screenshot。

コードの計測
#include "Stats/Stats.h"                              // DECLARE_*_STAT, SCOPE_CYCLE_COUNTER
#include "HAL/PlatformMisc.h"                         // SCOPED_NAMED_EVENT
#include "ProfilingDebugging/CpuProfilerTrace.h"      // TRACE_CPUPROFILER_EVENT_SCOPE
#include "ProfilingDebugging/MiscTrace.h"             // TRACE_BOOKMARK
#include "ProfilingDebugging/CountersTrace.h"         // TRACE_*_COUNTER, TRACE_INT_VALUE
#include "ProfilingDebugging/CsvProfiler.h"           // CSV_*
#include "ProfilingDebugging/ScopedTimers.h"          // FScopedDurationTimer

DECLARE_STATS_GROUP(TEXT("MyGame"), STATGROUP_MyGame, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("MyGame Tick"), STAT_MyGameTick, STATGROUP_MyGame);
CSV_DEFINE_CATEGORY(MyGame, true);
TRACE_DECLARE_INT_COUNTER(MyGameActiveEnemies, TEXT("MyGame/ActiveEnemies"));

void UMySubsystem::Tick(float DeltaTime)
{
    SCOPE_CYCLE_COUNTER(STAT_MyGameTick);              // stat system + Insights
    TRACE_CPUPROFILER_EVENT_SCOPE(UMySubsystem::Tick); // Insights only, very cheap
    CSV_SCOPED_TIMING_STAT(MyGame, SubsystemTick);     // one CSV column
    SCOPED_NAMED_EVENT(MyGame_Tick, FColor::Orange);   // also visible to PIX/Razor

    TRACE_COUNTER_SET(MyGameActiveEnemies, ActiveEnemyCount);
    CSV_CUSTOM_STAT(MyGame, Enemies, ActiveEnemyCount, ECsvCustomStatOp::Set);
}

void UMySubsystem::BeginWave(int32 WaveNumber)
{
    TRACE_BOOKMARK(TEXT("WaveStarted_%d"), WaveNumber);  // vertical marker in Insights
    CSV_EVENT(MyGame, TEXT("WaveStarted"));

    double LoadSeconds = 0.0;
    {
        FScopedDurationTimer Timer(LoadSeconds);        // accumulates into LoadSeconds
        LoadWaveAssets(WaveNumber);
    }
    UE_LOGFMT(LogMyGame, Display, "Wave {Wave} loaded in {Seconds}s",
        ("Wave", WaveNumber), ("Seconds", LoadSeconds));
}

QUICK_SCOPE_CYCLE_COUNTER(STAT_MyAdHocScope) は、その場限りの計測用にインラインで stat を宣言します。TRACE_INT_VALUE(TEXT("MyGame/Spawned"), Count) と TRACE_FLOAT_VALUE は、宣言なしでカウンターを出力します。SCOPED_NAMED_EVENT_TEXT("Name", FColor::Yellow) はリテラルを受け取り、SCOPED_NAMED_EVENT_F は書式指定して出力します。

stat コマンド

まず stat unit を実行します。フレーム時間を Game、Draw、RHIT、GPU に分割して表示します。次に、stat game、stat scenerendering、stat initviews、stat slate、stat threading、stat streaming、stat net、または独自のグループ用の stat MyGame を使って、負荷が最も高いスレッドを詳しく調べます。stat unitgraph は同じ数値を時間経過に沿ってグラフ表示し、stat fps はフレームレートを表示し、stat hitches はスパイクを検出し、stat namedevents は外部ツールで使えるよう stat 名をプロファイラーイベントとして出力します。

メモリ
stat memory / stat memoryplatform    # engine categories / OS physical and virtual
memreport -full                      # dump to Saved/Profiling/MemReports
obj list class=Texture2D             # loaded objects of a class, with sizes

Memory Insights を使うには、-LLM と -trace=memalloc,callstack,module を指定して起動します。LLM_SCOPE(ELLMTag::EngineMisc)、または LLM_DEFINE_TAG/LLM_DECLARE_TAG で宣言したカスタムタグを LLM_SCOPE_BYTAG で指定して、割り当てをスコープ化します。Windows では、エディター以外のビルドはデフォルトで MallocBinned3 を使用します。-binnedmalloc2、-binnedmalloc、-ansimalloc、-stompmalloc で切り替えられます。

ヒッチキャプチャと UObject の数

snapshothitches -start は、stats システムがヒッチを検出するたびに自動でトレーススナップショットを取得するよう設定します。snapshothitches -stop は設定を解除します。これには STATS と Shipping 以外のビルドが必要です。また、設定中は Screenshot チャンネルを無効にするため、古いスクリーンショットがスナップショットの末尾に入ることはありません。

CSV プロファイリングを有効にした状態で CSV_TRACK_UOBJECT_COUNT=1 を指定してコンパイルすると、UObjectStats::GetUObjectCount() から取得した実行中の UObject 数を、毎フレーム、カテゴリ ObjectCount の CSV stat Total として記録します。フルメモリキャプチャを行わずに、長時間のセッションでオブジェクトリークを検出するために使用します。

デバッグビジュアライゼーション

デバッグ描画

DrawDebugHelpers.h のすべての機能は ENABLE_DRAW_DEBUG の条件下にあり、これは UE_ENABLE_DEBUG_DRAWING = (!(UE_BUILD_SHIPPING || UE_BUILD_TEST) || WITH_EDITOR) です。Test または Shipping のゲームビルドでは、空のインラインスタブになります(DrawDebugHelpers.h:180-185。SHIPPING_DRAW_DEBUG_ERROR=1 を定義するとこれらが取り除かれ、残った呼び出しはコンパイルに失敗します)。ただし、引数は引き続き評価されるため、コストの高い呼び出し箇所は #if ENABLE_DRAW_DEBUG で囲んでください。

#include "DrawDebugHelpers.h"

// LifeTime: -1 (default) or 0 draws one frame; > 0 is seconds.
// bPersistentLines = true keeps the shape until FlushPersistentDebugLines.
UWorld* World = GetWorld();
DrawDebugLine(World, Start, End, FColor::Red, false, 2.0f, 0, 2.0f);
DrawDebugPoint(World, Location, 8.0f, FColor::White, false, 2.0f, 0);
DrawDebugDirectionalArrow(World, Start, End, 40.0f, FColor::Cyan, false, 2.0f, 0, 2.0f);
DrawDebugBox(World, Center, Extent, FQuat::Identity, FColor::Blue, false, 2.0f, 0, 1.0f);
DrawDebugSphere(World, Center, 100.0f, 12, FColor::Green, false, 2.0f, 0, 1.0f);
DrawDebugCapsule(World, Center, 88.0f, 34.0f, FQuat::Identity, FColor::Yellow, false, 2.0f, 0, 1.0f);
DrawDebugString(World, Location, TEXT("Label"), nullptr, FColor::White, 2.0f, true, 1.0f);
FlushPersistentDebugLines(World);   // and FlushDebugStrings(World)

Blueprint から呼び出せる UKismetSystemLibrary::DrawDebugLine/Arrow/Sphere/Capsule/String は、それぞれ独自に条件判定を行い、FLinearColor と EDrawDebugSceneDepthPriorityGroup を受け取ります。また、PrintString(WorldContextObject, InString, bPrintToScreen, bPrintToLog, TextColor, Duration, Key) も利用できます。

Visual Logger

ENABLE_VISUAL_LOG は PLATFORM_DESKTOP && !NO_LOGGING && UE_ENABLE_DEBUG_DRAWING です。Window > Visual Logger からタイムラインを開きます。

#include "VisualLogger/VisualLogger.h"

UE_VLOG(this, LogMyGame, Log, TEXT("State: %s"), *StateName);
UE_VLOG_UELOG(this, LogMyGame, Warning, TEXT("Lost target %s"), *TargetName);  // also hits UE_LOG
UE_VLOG_LOCATION(this, LogMyGame, Verbose, GetActorLocation(), 20.0f, FColor::Green, TEXT("Here"));
UE_VLOG_SEGMENT(this, LogMyGame, Log, PathStart, PathEnd, FColor::Red, TEXT("Path"));
UE_VLOG_SPHERE(this, LogMyGame, Verbose, PatrolPoint, 50.0f, FColor::Blue, TEXT("Patrol"));
UE_VLOG_ARROW(this, LogMyGame, Log, GetActorLocation(), AimPoint, FColor::Yellow, TEXT("Aim"));  // also _BOX, _CAPSULE
UE_CVLOG(bIsAlerted, this, LogMyGame, Log, TEXT("Alerted"));   // also UE_CVLOG_SEGMENT, UE_CVLOG_LOCATION

FVisualLogger::Get().SetIsRecording(true);       // and SetIsRecordingToFile / SetIsRecordingToTrace

各マクロは FVisualLogger::IsRecording() を使って早期に処理を抜けるため、記録中でなければ引数のコストはかかりません。

Gameplay Debugger と ShowDebug

PIE で ' を押します。モジュールの StartupModule からカテゴリを登録します(PrivateDependencyModuleNames に "GameplayDebugger" を追加し、登録処理とカテゴリクラスを #if WITH_GAMEPLAY_DEBUGGER で囲みます。Test/Shipping ゲームビルドでは、デフォルトで 0 です — GameplayDebuggerCategory.h:10-11、TargetRules.cs:1125):

#include "GameplayDebugger.h"   // in FMyGameModule::StartupModule()
if (IGameplayDebugger::IsAvailable())
{
    IGameplayDebugger& Debugger = IGameplayDebugger::Get();
    Debugger.RegisterCategory(TEXT("MyGame"),
        IGameplayDebugger::FOnGetCategory::CreateStatic(&FMyDebuggerCategory::MakeInstance),
        EGameplayDebuggerCategoryState::EnabledInGame);
    Debugger.NotifyCategoriesChanged();
}

FMyDebuggerCategory は FGameplayDebuggerCategory を継承し、virtual void CollectData(APlayerController* OwnerPC, AActor* DebugActor) と virtual void DrawData(APlayerController* OwnerPC, FGameplayDebuggerCanvasContext& CanvasContext) をオーバーライドします。それらに AddTextLine と AddShape を渡します。ShutdownModule で UnregisterCategory(TEXT("MyGame")) を呼び出します。

AHUD::ShowDebug(FName DebugType) は名前付きデバッグページ(ShowDebug AI、ShowDebug Physics)を切り替えます。ShowDebugToggleSubCategory(FName) はサブページを切り替え、ShowDebugForReticleTargetToggle(TSubclassOf<AActor>) はレティクルが指している対象に表示先を切り替えます。独自のページを追加するには、virtual void ShowDebugInfo(float& YL, float& YPos) をオーバーライドするか、AHUD::OnShowDebugInfo にバインドします。別個の display、displayall、displayclear コンソールコマンドは、オブジェクトまたはクラスの名前付きプロパティを HUD に出力します。

コンソールコマンドと CVar

// In the AMyPlayerController class body. Exec works on PlayerController, Pawn, HUD,
// GameMode, GameState, CheatManager (or a UCheatManagerExtension) and GameInstance.
UFUNCTION(Exec)
void ToggleGodMode();
// MyGameDebug.cpp — console objects register at static init
#include "HAL/IConsoleManager.h"

static FAutoConsoleCommand GMyDumpStatsCmd(
    TEXT("MyGame.DumpStats"), TEXT("Dump gameplay stats"),
    FConsoleCommandDelegate::CreateStatic(&FMyStatsDumper::Dump));

// World + args; prefer this for anything that touches gameplay in PIE.
static FAutoConsoleCommandWithWorldAndArgs GMySpawnItemCmd(
    TEXT("MyGame.SpawnItem"), TEXT("Usage: MyGame.SpawnItem <Name>"),
    FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&FMySpawner::SpawnFromConsole));

static TAutoConsoleVariable<float> CVarMyDamageScale(
    TEXT("MyGame.DamageScale"), 1.0f, TEXT("Scales all outgoing damage."), ECVF_Cheat);

// FAutoConsoleVariableRef binds a console name to an existing int32/float/bool/FString/FName.
static float GMyDrawDistance = 5000.0f;
static FAutoConsoleVariableRef CVarMyDrawDistance(
    TEXT("MyGame.DrawDistance"), GMyDrawDistance, TEXT("Gameplay draw distance."), ECVF_Default);

CVar は GetValueOnGameThread()、GetValueOnRenderThread()、または GetValueOnAnyThread() で読み取ります。一般的なフラグ:ECVF_Default、ECVF_Cheat(Shipping ターゲットでは無効)、ECVF_ReadOnly、ECVF_Scalability、ECVF_RenderThreadSafe。コマンドの有効期間がオブジェクトに紐づく場合は、実行時に IConsoleManager::Get().RegisterConsoleCommand(Name, Help, Delegate) で登録し、返された IConsoleCommand* を IConsoleManager::Get().UnregisterConsoleObject(Cmd) で解放します。

非推奨 — 使用しないでください

出力しないもの5.8 で使用するものソース
UE_TRACE_BOOKMARK(...)TRACE_BOOKMARK(Format, ...)ProfilingDebugging/MiscTrace.h — UE_TRACE_BOOKMARK は存在しません
Trace.Start [Channels]Trace.File [Path] [Channels]TraceAuxiliary.cpp の「'Trace.Start' is being deprecated in favor of 'Trace.File'」
stat startfile / stat stopfile / stat startfileraw(.uestats)Trace.File + UnrealInsights.exeStats/StatsFile.h の UE_ENABLE_STATS_FILE_DEPRECATED_IN_5_8(デフォルトは 0)で制御されています
stat gpuProfileGPU、DumpGPU、またはキューごとのグループ stat GPU0_Graphics0GPU グループは RHI/Private/GPUProfiler.cpp で GPU%d_%s%d から STATGROUP_%s として構築されます
EditorContext のようなスコープなしのフラグ名スコープを付けます:EAutomationTestFlags::EditorContextEAutomationTestFlags は Misc/AutomationTest.h の enum class です
独立した型としての EAutomationExpectedErrorFlags::…EAutomationExpectedMessageFlags::…前者は現在、後者の名前空間エイリアスです
UE_LOG_REF、UE_LOG_CLINKAGEUE_LOGLogging/LogMacros.h で「DO NOT USE … will be deprecated」とマークされています
-csvstatfile=Out.csv-csvCaptureFrames=N と CsvProfile StartFile=<name>CsvProfiler.cpp は csvCaptureFrames=、csvCategories=、csvMetadata=、csvGpuStats、csvExecCmds= などを解析しますが、csvstatfile はありません

よくある間違い

Test ビルドでも check/ensure が有効だと思い込む: DO_CHECK と DO_ENSURE は、Test と Shipping の両方で USE_CHECKS_IN_SHIPPING/USE_ENSURES_IN_SHIPPING に対応しており、どちらもデフォルト値は 0 です。式を必ず実行する必要がある場合は verify() を使用するか、bUseChecksInShipping を設定してください。

ログやアサートの中にロジックを記述する: UE_LOG(LogMyGame, Log, TEXT("%d"), AdvanceCounter()); は NO_LOGGING が有効になるとカウンターの更新を停止し、check(Init()) は DO_CHECK が 0 になると初期化を停止します。まずローカル変数に計算結果を格納してから、それをログに出力するかアサートしてください。

コンソール出力を期待して Log を使う: コンソールに出力するのは Display であり、Log はログファイルにのみ出力します。「ログ行が表示されない」という場合、ほとんどは Log の詳細度のメッセージです。

フィルターフラグがない: すべての IMPLEMENT_*_AUTOMATION_TEST、DEFINE_SPEC、BEGIN_DEFINE_SPEC には、SmokeFilter/EngineFilter/ProductFilter/PerfFilter/StressFilter/NegativeFilter のいずれか 1 つと、少なくとも 1 つのコンテキストフラグが必要です。そうでない場合、static_assert によってコンパイルに失敗します。

CQTest のアサーション結果を破棄する: アサーターのメソッドは [[nodiscard]] のため、Assert.IsTrue(x); と書くと警告が発生し、失敗後も処理が続行します。必ず ASSERT_THAT(IsTrue(x)); と記述してください。

Shipping または Test のゲームビルドで DrawDebug* を使う: 呼び出しは何もしないスタブになりますが、引数(トレースや文字列のフォーマット)は引き続き実行されます。ブロック全体を #if ENABLE_DRAW_DEBUG で囲んでください。

Super::StartTest() を呼び出さない: AFunctionalTest::StartTest は Blueprint の ReceiveStartTest イベントを呼び出すため、Super:: を省略すると、同じテストアクター内の Blueprint で記述されたすべてのステップが気づかないうちに無効になります。

関連スキル

  • ue-cpp-foundations — UObject の基本、デリゲート、コンテナー、UFUNCTION/UPROPERTY 指定子の一覧
  • ue-module-build-system — Build.cs と Target.cs の設定、bBuildDeveloperTools、テストモジュールのセットアップ、プラグインの構成
  • ue-editor-tools — テストおよびデバッグ UI をホストするエディターユーティリティ、コマンドレット、Slate ツール
  • ue-physics-collision — 結果を DrawDebug* で可視化するトレースおよびスイープクエリー
  • ue-ai-navigation — ビヘイビアツリー、EQS、AI 側の Gameplay Debugger カテゴリー
  • ue-async-threading — 潜在コマンドおよび task トレースチャンネルで監視するタスクグラフと非同期処理
  • ue-gameplay-framework — Exec 関数をホストする GameMode、PlayerController、HUD、CheatManager
  • ue-materials-rendering — マテリアルインスタンス、パラメーターコレクション、レンダーターゲット、ポストプロセス

コメント