攻撃が当たったように見えるのに、敵のHPが減らない。そんなとき、見た目だけでは「ダメージ処理が呼ばれていない」のか、「呼ばれたけれど無敵扱いになった」のかを区別できません。
そこで、処理の途中に短いメッセージを出してみます。「ここまで来た」「ダメージは25」「無敵なのでスキップ」。内部で起きたことが見えると、次に調べる場所を絞れます。このように動作を調べて不具合を直す作業が デバッグ、その手がかりとして残す記録が ログ です。
この記事では、Blueprintの Print String でメッセージを出し、エディタの Output Log で読み返す方法を試します。実践はActorを1つ置けば始められるので、武器や敵の仕組みがまだなくても進められます。
この記事でわかること
- Print Stringで処理の到達と変数の値を確かめる
- Output Logから自分のメッセージを探す
- 「呼ばれたか・値はいくつか・どちらへ進んだか」で原因を絞る
- 繰り返し表示するKeyと、C++のログ出力を使い分ける
Print Stringで、まず1行出してみる
Print Stringは、指定した 文字列 を出力するノードです。文字列とは、Hello や HP: 100 のような、ひとまとまりの文字のこと。画面の左上に出すか、Output Logへ記録するかを選べます。

まずは実践でも使う、小さな確認用Actorを作りましょう。Actorはレベルに配置できるもの です。今回はメッセージを出す役なので、見た目のモデルは付けません。
- コンテンツブラウザで「Blueprint Class」を作成し、親クラスに「Actor」を選び、
BP_DebugEnemyと名 付けます。 - 開いて「Event Graph」へ移動します。ここが、処理を表すノードをつなぐ作業場所です。
Event BeginPlayの右側にある白い実行ピンから線をドラッグし、Print Stringを検索して追加します。BeginPlayは、このActorのゲーム中の動作が始まるときに呼ばれるイベントです。- Print Stringの「In String」に
[DebugEnemy] Startを入力します。ノード下部の展開矢印から「Print to Screen」「Print to Log」をオン、「Duration」を5.0にします。 - 「Compile」「Save」を押し、コンテンツブラウザから
BP_DebugEnemyをレベルへ 1体だけ ドラッグします。レベルも保存して「Play」を押します。
左上に [DebugEnemy] Start と出れば成功です。[DebugEnemy] は自分で付けた目印で、後からこの実験のログを検索するために使います。Actorにはモデルがないため、画面に敵の姿が出なくても問題ありません。
白い線は 処理を実行する順番 を表します。Print Stringを置いただけでは動かず、BeginPlayから白い線がつながっていることが必要です。
| 設定 | 今回の役割 |
|---|---|
| In String | 出したい文字列 |
| Print to Screen | ゲーム画面へ表示する |
| Print to Log | 通常のログとしてOutput Logへ出す |
| Text Color | 画面に出す文字の色 |
| Duration | 画面に残る秒数。5.0 なら5秒 |
| Key | 同じ画面メッ セージを更新するための名前。実践では None のまま |
Output Logで、消えたメッセージを読み返す
Print Stringの画面表示は時間がたつと消えます。表示を見逃したときや、複数の処理を順に読みたいときは Output Log(出力ログ) を開きます。自分が出したメッセージに加えて、UEが出した警告やエラーも確認できる画面です。

- 「Stop」でゲームを止め、「Window → Output Log」を開きます。
- 検索欄へ
DebugEnemyと入力します。 [DebugEnemy] Startを探します。画面で消えてしまった文字を、ここで読み返せれば成功です。
見つからなければ、Print Stringの「Print to Log」がオンか、Output Logのフィルタがエラーや警告だけに絞られていないかを確認します。ログがないように見えても、フィルタで隠れているだけの場合があります。
ログの先頭にある LogTemp などは カテゴリ、つまり「どの種類の処理が出した記録か」を分ける名前です。慣れないうちはカテゴリを覚えるより、今回のように自分で付けた目印から探すと進めやすいでしょう。
実践:HPが減らない理由を調べる
次は BP_DebugEnemy に、HPから25を引く処理を足します。ただし、最初はわざと「無敵」をオンにしておきます。ログを読み、HPが減らない理由を確かめてから直してみましょう。

図はゲームでの使用場面です。今回の練習では攻撃や当たり判定を作らず、BeginPlayから1回だけダメージ処理を動かします。 これで、ダメージ計算を調べる準備に集中できます。
準備:調べる値を3つ用意する
Playを止め、BP_DebugEnemy の「My Blueprint → Variables」の「+」から次の変数を作ります。変数は、HPなどの値を覚えておくためのものです。「Compile」後、各変数の「Default Value」に初期値を設定します。
| 変数名 | 型 | 初期値 | 意味 |
|---|---|---|---|
Health | Float | 100.0 | 現在のHP |
IncomingDamage | Float | 25.0 | 今回受けるダメージ量 |
bIsInvincible | Boolean | true(チェックあり) | 無敵ならtrue、ダメージを受けるならfalse |
Float は小数を扱える数値、Boolean は true / false の2つだけを持つ型です。今回は「無敵かどうか」をBooleanで表します。変数名の先頭の b はBooleanだと分かるように付けた目印で、特別な動作はしません。

① ここまで呼ばれたか、値はいくつか
最初に作った [DebugEnemy] Start は、BeginPlayから処理が来たことを示します。その後ろへ、ダメージ量を出すPrint Stringを追加します。

図では見やすさのため、値の線を青で統一しています。実際のエディタでは値の型によって色が変わります。「次:Branch」は次の図への案内で、追加するノードではありません。
- 1つ目のPrint Stringの白い出力ピンから、2つ目のPrint Stringへつなぎます。
IncomingDamageをグラフへドラッグし、「Get」を選びます。Getは変数 に入っている値を読み出す操作です。- 文字列をつなぐ
Appendノードを置き、Aに[DebugEnemy] Damage:を入力します。 - IncomingDamageの出力をAppendのBへつなぎます。数値を文字列に変換するノードが自動で入ります。Appendの「Return Value」を2つ目のPrint Stringの「In String」へつなぎます。
Return Valueは、そのノードが作った結果 です。ここでは、文字と数値を合わせた [DebugEnemy] Damage: 25 という文字列が入ります。小数点以下の表示は変換の設定で変わるため、25 でも 25.0 でも同じ値です。
2つ目も画面・ログ出力をオン、Durationを 5.0 にしてCompile・Saveし、Playします。Output Logで DebugEnemy を検索し、StartとDamageの2行を順に確認してください。ここまでで「処理が始まり、ダメージの値は25だった」と分かります。
② どちらの条件へ進んだか
Playを止め、2つ目のPrint Stringの後ろに Branch を置きます。Branchは条件に応じて処理の進み先を分けるノードです。「Condition」へ bIsInvincible のGetをつなぎます。
今回は「無敵か」をそのまま聞くため、True側がスキップ、False側がダメージ適用 です。

- True側:Print Stringへつなぎ、In Stringを
[DebugEnemy] Skip: Invincibleに します。 - False側:
Set Healthへつなぎます。変数Healthをグラフへドラッグし「Set」を選ぶと置けます。Setは変数へ新しい値を書き込む操作です。
Set Healthへ渡す値は、Floatの減算ノードで作ります。HealthのGetから線をドラッグして Subtract(-)を追加し、上の入力をHealth、下の入力をIncomingDamage にします。計算結果の出力をSet Healthの値の入力へつなぎます。

最後に、Set Healthの白い出力からPrint Stringを追加します。先ほどと同じようにAppendを置き、Aを [DebugEnemy] HP: 、BをSet Healthの値の出力につなぎます。数値から文字列への変換を挟み、AppendのReturn ValueをPrint StringのIn Stringへ渡します。これで、書き込んだ後のHP を表示できます。追加したPrint Stringも、画面・ログ出力をオン、Durationを 5.0 にします。

図のSet Healthは、ひとつ前の図と同じノードです。新しくもう1つ置く必要はありません。
③ ログを比べて、1か所だけ直す
Compile・SaveしてPlayすると、最初は次の3行が出ます。画面では新しい行が上に積まれることがあるので、順序はOutput Logで確かめます。前のPlayのログも残るため、末尾の今回分を見てください。
[DebugEnemy] Start
[DebugEnemy] Damage: 25
[DebugEnemy] Skip: Invincible
ダメージ量は25でしたが、無敵の分岐へ進んでいます。今回HPが減らなかったのは、計算前にスキップしていたため です。
Playを止め、Blueprintの bIsInvincible の初期値を false(チェックなし)に変えます。Compile・Saveしてもう一度Playすると、Skipの代わりに [DebugEnemy] HP: 75 が出ます。100から25を引いたHPが、実際に書き込まれました。
次に、Playを止めて IncomingDamage の初期値だけを 40.0 に変えてみてください。再びPlayしたとき、Damageは40、更新後のHPは60になるはずです。毎回Playし直すので、Healthは初期値100から始まります。
自分の攻撃処理でも、同じ順番で調べる
実際のゲームでは、攻撃を受けたときの処理の先頭へStartに相当するログを置きます。UEのダメージ機能を使っているなら、入口は Event AnyDamage です。衝突を知らせるHitや重なりを知らせるOverlapとは役割が違うので、自分の処理の入口に合わせます(→ 体力とダメージの作り方)。
| 確認したこと | 次に調べる場所 |
|---|---|
| 最初のログが出ない | 表示設定・ログのフィルタ、対象Actorの存在、イベントへ至る接続 |
| 処理は来た がダメージが0 | ダメージを渡す側と、値を読み出す場所 |
| 値は正しいがスキップする | 無敵などの条件と、その値を変えている処理 |
| 更新後のHPは減るが、HPバーは変わらない | 表示を更新する処理と、表示側が参照している敵 |
ログが1つ出ただけで原因のすべてが分かるわけではありません。それでも、確認できた地点より先へ調査を進められる ので、無関係な設定をまとめて変更せずに済みます。
Keyで画面の同じ行を更新する
移動速度など、変わり続ける値を短時間追いたいこともあります。毎フレーム、つまり画面が1回更新されるたびに呼ばれる Event Tick へPrint Stringを置くと、何行もの文字で画面が埋まってしまいます。
そのとき使うのが Key です。PlayerSpeed などNone以外の名前を指定すると、同じKeyを持つ画面メッセージが上書きされます。

Keyは 画面表示 の整理 に使うものです。Output Logの記録を1行にまとめたり、Durationを超えて文字を残したりする機能ではありません。Tickで一時的に値を見る場合は「Print to Log」をオフにし、通常のログが大量に増えるのを避けます。HPのように変更した瞬間を知りたい値なら、まずは変更処理の直後に出す方法で十分です。
C++でのログ出力:UE_LOG
C++で同じように値を記録するときは、UE_LOG を使います。Blueprintで進めている方は、必要になってから戻ってきて構いません。
たとえば、自作Actorの .cpp ファイルで、既存の BeginPlay() 内の Super::BeginPlay(); より後へ次の2行を足すと、ログにHPを出せます。
const float HealthForDebug = 100.0f;
UE_LOG(LogTemp, Log, TEXT("[DebugEnemy] HP: %.1f"), HealthForDebug);
LogTemp は一時的な確認に使えるカテゴリ、2つ目の Log は記録の重要度です。%.1f の位置には数値が小数点以下1桁で入り、HP: 100.0 と出ます。TEXT(...) はUEで文字列を扱うための書き方です。独自カテゴリの用意は、ログが増えてからでも間に合います。
Verbosity はログの重要度・詳しさの区分です。問題を表すログと普段の記録を分けておくと、Output Logで絞り込みやすくなります。

| 区分 | 用途の目安 |
|---|---|
| Error | 必要なデータを読み込めなかった、などの問題 |
| Warning | 想定外の値を補正した、など確認しておきたい状態 |
| Display | 起動時の設定など、コンソールにも出したい情報 |
| Log | 今回のHPのような、普段の動作確認 |
Errorは問題を記録するもので、それだけでゲームを停止させる命令ではありません。致命的なエラーを記録してクラッシュさせる Fatal は別の区分です。また、printf でも文字を出せる場面はありますが、UEのカテゴリ・重要度・ログ画面と一緒に扱うにはUE_LOGが適しています。
調べやすいログを残すコツ
- 名前と値をセットにする:
25だけよりDamage: 25の方が後から意味を読めます。複数の敵を調べるならActor名も添えます。 - 変更は1つずつ試す:今回なら無敵の初期値を直してから、ダメージ量を変えます。結果が変わった理由を見失いません。
- 普通の記録も表示する:自分のPrint Stringを探すときは、Error・Warningだけの絞り込みを解除します。逆に、UE側の問題を探すときはそれらに絞ると読みやすくなります。
- 確認用の処理を片付ける:実験後は、作業用レベルからBP_DebugEnemyを取り除きます。自分のゲームへ入れた一時ログも、役目を終えたものから削除します。
おまけ:先に知っておくと良いこと
位置や範囲は、図で確かめる。 ログで「攻撃を調べる処理は動いた」と分かっても、判定範囲が敵まで届いているかは文字だけでは追いづらいものです。線や球をゲーム画面へ描く Draw DebugとVisual Logger を組み合わせると確認しやすくなります。
- Accessed Noneの意味:操作しようとした相手が指定されていないなど、使える参照がないときの手がかりです。ログに出たBlueprintやノードを開き、相手を取得する処理から調べます。
- Print Stringは開発中の確認用:通常のShippingビルドでは使えません。製品のHP表示や、プレイヤーへの通知の代わりには使わないでください。
- ファイルからも読める:通常のエディタ実行では、プロジェクトの
Saved/Logs/にログが保存されます。閉じた後に調べるときは日時の合うファイルを確認します。 - 止めて調べる方法もある:場所が絞れたら、処理を一時停止して変数を見られる Blueprint Debugger が役立ちます。
まとめ
Print Stringで処理の途中に目印を置き、Output Logで順番と値を読み返す。この2つを組み合わせると、見た目だけでは分からない動きを追えます。
まず確かめるのは「ここまで呼ばれたか」「値はいくつか」「どちらへ進んだか」の3つです。今回の無敵設定のように、小さく試して結果を比べながら、調べる場所を絞っていきましょう。