【Unreal Engine】Print StringとOutput Logを使ったデバッグ手法

作成: 2025-12-12最終更新: 2026-07-19

UE5デバッグの基本、BlueprintのPrint StringとOutput Log・UE_LOGの使い方を図解。Keyによる上書き表示、Verbosityの使い分け、「当たったのにダメージが入らない」を切り分ける実践つき。

「エラーは出ていないのに、なぜか動かない」。UE開発で最も時間を奪うのは、この静かなバグです。

原因を推理で当てようとすると迷宮入りします。必要なのは推理ではなく 証拠集め 。そのための最初の道具が、Blueprintの Print String とエディタの Output Log です。この記事では、2つの道具の使い方と使い分け、そして「攻撃が当たっているのにダメージが入らない」という定番のバグを証拠から切り分ける手順を解説します。

虫眼鏡で足跡を追い、ログの画面へたどり着く人形。デバッグのイメージ

この記事でわかること

  • Print String の使い方と、 Key による画面表示の上書きテクニック
  • Output Log の開き方・フィルタリング・ログカテゴリ
  • C++の UE_LOG マクロと Verbosity(重要度) の使い分け
  • 実践:「当たったのにダメージが入らない」を 3つの質問で切り分ける

Sponsored

Print String は、指定した文字列をゲーム画面の左上(とOutput Log)に出す、最も手軽なデバッグノードです。処理の流れの途中に挿むだけで、「この処理は本当に実行されたか」「この変数はいま何か」を目で確かめられます。

Print Stringは画面でさっと確かめる、Output Logはログでじっくり調べる。手軽さの画面、確実さのログ
Event Jump → Print String(In String: "Jump! Velocity: " に Get Velocity を Append)
パラメータ役割使いこなし
In String表示する文字列どの処理からの出力か分かる印を付ける(例: [Jump]
Print to Screen / Log画面/Output Logへの出力切替基本は両方ON。画面が邪魔ならLogのみに
Text Color画面表示の色重要なメッセージだけ目立つ色に
Duration画面に残る秒数(既定2.0)流れの速い処理を追うときは5.0などへ
Keyメッセージの識別キー同じKeyのメッセージは上書きされる(下記)

Keyを使った値の追跡

Key は、Print Stringをデバッグツールとして一段強くする仕掛けです。TickでHPを表示するような「毎フレーム出る」ログは、そのままでは画面を埋め尽くします。同じKeyを設定すれば、 画面には常に最新の1行だけ が表示され、変数の変化をカウンターのように追跡できます。

Keyなしではメッセージが積もって画面を埋め、Keyありでは最新の1行だけが上書き表示される
Sponsored

Output Logの活用

画面のPrint Stringは数秒で消えるが、Output Logには残って後から検索できる、という対比図

Print Stringが「その場の目視確認」なら、 Output Log はUE全体の情報が集まる デバッグの心臓部 です。自分のログだけでなく、エンジンの警告・エラー・アセットのロード情報まで、すべてがここに時系列で記録されます。

  • 開き方: メニューの ウィンドウ → 出力ログ(Output Log)
  • フィルタリング: 上部の検索バーでキーワード絞り込み。Verbosityフィルタで Errors / Warnings だけの表示に切り替えられます
  • ログカテゴリ: ログは LogTempLogBlueprintLogAI などのカテゴリ付きで記録されます。「どのシステムの発言か」で絞り込めます

Blueprintだけで開発している間も、 「動かない時はまずOutput LogのErrorとWarningを見る」 を習慣にしてください。画面のPrint Stringは流れて消えますが、ログには全部残っています。

Sponsored

C++でのログ出力:UE_LOGマクロ

C++側のPrint Stringに相当するのが UE_LOG マクロです。printfstd::cout はUEのログシステムに乗らないため使いません。

// ログカテゴリを定義(プロジェクト用に1つ作ると絞り込みが楽になる)
DEFINE_LOG_CATEGORY_STATIC(LogMyGame, Log, All);

// 警告レベルで、変数を埋め込んで出力
UE_LOG(LogMyGame, Warning, TEXT("Player %s damaged. HP: %f"), *PlayerName, CurrentHealth);

// エラーレベル+早期リターン(Nullチェックの定番形)
if (TargetActor == nullptr)
{
    UE_LOG(LogMyGame, Error, TEXT("TargetActor is null!"));
    return;
}

第二引数が Verbosity(重要度) です。重要度で色分けしておくと、あとからOutput Logのフィルタで「ErrorとWarningだけ見る」が効くようになります。

ErrorからLogまでの4段階と、重要度でふるい分ける漏斗の図。色分けは、あとで絞り込むための投資
Verbosity役割用途の例
Error致命的な問題(赤字で表示)必須リソースのロード失敗、null参照
Warning問題の兆候(黄字で表示)予期しない入力値、非推奨機能の使用
Displayコンソールにも出したい情報起動時の設定値の記録など
Log標準の記録処理の開始・終了、値の確認

補足: ほかに、出力した瞬間にクラッシュさせる Fatal、普段は非表示の詳細用 Verbose / VeryVerbose もあります。まずは表の4つで十分です。

Sponsored

実践:「当たったのにダメージが入らない」を切り分ける

道具が揃ったところで、定番のバグを1つ捜査してみましょう。シューティングの弾でも、アクションの剣でも、RPGの魔法でも構いません——「攻撃がヒットしているように見えるのに、敵のHPが減らない」。あの現象です。

プレイヤーの弾が敵に当たって火花が出ているのに、敵のHPバーは満タンのまま。当たっている。なら、なぜ減らない?

推理で「たぶんコリジョンかな……」と当てずっぽうにいじり始める前に、Print Stringで 3つの質問 に順番に答えさせます。

再現用の準備(手元に該当グラフがない方は、これだけ作れば試せます)。敵Blueprint BP_Enemy に次の変数を用意します。

変数名初期値役割
HealthFloat100.0敵の体力
IncomingDamageFloat25.0受け取ったダメージ量。武器から渡ってくる値のつもりで、ここに直接入れておきます
bIsInvincibleBooleantrue無敵フラグ。 このtrueがわざと仕込んだ不具合 です
切り分けの3ステップ。呼ばれたか、値はいくつか、どっちへ進んだか。疑いは順番に、1つずつ潰す

ログを仕込んだあとのグラフは、この形になります。①と②の位置を頭に入れてから読み進めてください。

Event OnHitの直後とBranchの両側にPrint Stringを置いたノードグラフ。Notを通したbIsInvincibleがBranchのConditionへ入る
  1. 質問①「そのイベント、呼ばれてる?」: ヒット処理の先頭に、まずログを1つ挿します

    Event OnHit(Overlap や Any Damage など)
      → Print String(In String: "Hit!")      ← ★質問①
      → Branch(Condition: Not(bIsInvincible))
    

    Playして攻撃してみてください。画面に Hit! が出なければ、犯人はダメージ計算より手前、 当たり判定そのもの です(→ カスタムコリジョンチャンネル

  2. 質問②「値はいくつ?」: Hit! が出たなら、次はダメージ量を数字で見ます。文字列と数値は Append で繋ぎます

    Print String(In String: Append(A: "Damage: ", B: ToString(IncomingDamage)))
    

    今回は Damage: 25.0 と出るはずです。数値が正しく渡っているので、犯人はもっと先にいます。もしここで Damage: 0.0 と出るようなら、処理そのものは動いていて 渡している数値がゼロ なだけなので、武器データの設定ミスが濃厚です

  3. 質問③「どっちへ進んだ?」: 値も正しいのに減らないなら、分岐の両側にログを1つずつ置いて、通った道を白状させます

    Branch(Condition: Not(bIsInvincible))
      → True  → Print String("通った: ダメージ適用へ") → Health から IncomingDamage を引いて Set
      → False → Print String("通った: 無敵でスキップ")  ← ★今回はこちらが出る
    

    画面に 通った: 無敵でスキップ と出れば、犯人は bIsInvincible です。初期値をfalseに直して、もう一度攻撃してみてください。Health が100→75に減れば解決です

3つの質問はどれも30秒で仕掛けられて、答えが Yes/Noで返ってくる のが強みです。犯人の居場所が「当たり判定」「データ」「条件式」のどれかまで絞れたら、捜査は9割終わっています。仕上げにOutput Logを開いて、赤いエラー——特に Accessed None(空の参照を触った)——が出ていないかを確認しましょう。出ていれば、そのメッセージがバグの住所を教えてくれています。

ポイントは2つです。

  • 一度に1つだけ疑う: 「呼ばれたか → 値は → 分岐は」の順に、疑いを1つずつ潰します。まとめて疑うと、直ったときに何が原因だったのか分からなくなります
  • 捜査が終わったらPrint Stringを掃除する: 役目を終えたデバッグノードは削除するか、切断しておきます。次のバグのときに、古いログはノイズにしかなりません

ベストプラクティスとよくある間違い

プラクティス効果
ログに 出どころの印 を含める([MyActor] HP: 100大量のログでも発信元が一目で分かる
Verbosityを使い分ける(情報=Log/兆候=Warning/致命=Error)フィルタが効き、緊急度で対応できる
Tickでの追跡は 必ずKeyを設定画面が埋まらず、値の変化だけ追える
bDebugMode のような デバッグ用フラグ を用意し、ログの前でチェックフラグ1つで全ログを止められる

よくある間違いは3つあります。①Durationを既定の2秒のままにして「一瞬で消えた」と見逃す(長くするかKeyで常駐させる)、②画面のPrint Stringだけ見てOutput LogのError/Warningを見ない、③C++で printf を使う(必ずUE_LOGで)。

おまけ:先に知っておくと良いこと

文字で分からないものは、描く。 「検知範囲がどこまでか」「敵がどちらを向いているか」といった 空間の情報は、文字にしても分かりません 。そういうときは範囲や向きをゲーム画面の中に描いてしまうのが速いです(→ Draw DebugとVisual Logger)。

  • Print Stringは開発ビルド専用: パッケージ版(Shipping)では出力されません。「消し忘れたら製品に出てしまう」心配はない一方、Shipping版だけで起きる問題の調査には使えない、と覚えておきましょう
  • ログはファイルにも残っている: プロジェクトの Saved/Logs/ フォルダに、実行ごとのログファイルが保存されています。「さっき閉じてしまったあのエラー」も後から読めます
  • 次の武器はブレークポイント: Print Stringで場所を絞ったら、その行で実行を一時停止して変数の中身を覗ける Blueprint Debugger が次の道具です → Blueprint Debuggerの使い方

まとめ

  • Print String=画面でさっと、Output Log=ログでじっくり 。2つで1セット
  • Tickの追跡は Key で上書き表示。C++は UE_LOGVerbosity の色分けで「あとから絞れる」ログを書く
  • バグは推理せず、 「呼ばれたか → 値は → 分岐は」 の3つの質問で証拠を集めて切り分ける

デバッグの相棒ができたら、Blueprintでよくある10のミスで「そもそも踏みやすい地雷」を先回りして知っておくのもおすすめです。

いまあなたのプロジェクトで「なぜか動かない」ままになっている処理、3つの質問のどれから聞いてみますか?