攻撃が当たってHPは減っている。それでも、画面の隅に数値が出るだけでは「どの相手に、どれだけ効いたか」が分かりにくいものです。相手の頭上にバーが付き、攻撃のたびに「20」が浮かべば、手応えをその場で確かめられます。
この記事では、体力とダメージの記事で作ったCubeに、頭上HPバーとダメージ数字を足します。バーはCubeについて動き、数字は出現した画面位置からふわっと上がって消える。表示を付ける場所と、表示を終えるタイミングを分けて考えるのがポイントです。

図のバーは割合を見比べるため5区画で表しています。実践で作るのは、区切りのない1本のバーです。
この記事でわかること
- Widget Componentで、Actorの頭上にHPバーを付ける方法
- ScreenとWorldの見え方、入れ物と中身の違い
- 3Dの位置を画面の位置へ変え、ダメージ数字を出す手順
- 数字を上へ動かして薄くし、アニメーション終了後に片付ける方法
画面に置くUIと、Actorに付けるUI
Widget は、文字やバーなどを組み合わせたUIです。UMG入門では、作ったWidgetを Add to Viewport でゲーム画面へ追加しました。位置を変える処理を書かなければ、カメラを動かしても同じ画面位置に残ります。自分のHPやスコアを出すのに向いています。
一方、敵や箱などの Actor にUIを付ける部品が、Widget Component です。Actorに取り付けた表示の置き場所だと考えてください。Cubeの上へ配置しておけば、Cubeを動かしたとき、その置き場所も一緒に動きます。
Widget Componentの中には、さらに部品が入ります。今回なら「Cubeに付くWidget Component」「その中で表示するWBP_OverheadHealth」「バー本体のHealthFill」という3段階です。HPの割合を変える Set Percent は、最後の Progress Bar本体 に対して呼びます。

Get User Widget Object は、Widget Componentが表示している中身のWidgetを取り出すノードです。そこから Cast To WBP_OverheadHealth を使うと、その種類のWidgetに用意した関数を呼べます。Castは新しいバーを作る操作ではありません。 すでにある中身を、目的の型として扱うための確認です。
実践では、最初にWidgetを作ったときの戻り値を保存します。これで「今表示しているバー」を直接指定できるため、更新のたびに取り出し直す必要がありません。
ScreenとWorld:読みやすさと、世界の中の見え方
Widget Componentの Space は、UIをどこに描くかを決める設定です。同じActorに付いていても、見え方が変わります。

| Space | 見え方 | 使いどころ |
|---|---|---|
Screen | Actorの位置をもとに画面上へ表示。カメラを回 しても文字は傾かない | 頭上HPバー、名前 |
World | 3D空間に置いた面として表示。遠ざかると小さくなり、斜めから見ると傾く | 壁の操作パネル、端末の画面 |
Screenは読みやすい反面、手前に壁があっても、それだけではUIが隠れません。壁の裏の敵を見せたくないゲームでは、敵が見えるかどうかを調べて表示を切り替える処理が要ります。Worldは通常のメッシュと同じように遮蔽物の影響を受けますが、使うマテリアルの設定によって変わります。
今回はバーの読みやすさを優先してScreenを使います。Screenは「3Dの板を毎回カメラへ向ける」仕組みではなく、3Dの位置に合わせて画面へ描く方式です。
実践の準備:20ダメージを表示するCube
1人用のThird Personプロジェクトで、体力とダメージの記事の実践を作った状態から始めます。プレイヤーの E キーで正面の BP_Damageable に20ダメージを与え、5回目にCubeが消えるところまで確認してください。
今回はそのBlueprintアセットを複製し、BP_OverheadPractice と名付けます。レベルの練習用Cubeを、この複製したActorへ置き換えます。位置・回転・Scaleは元と同じにし、プレイヤーから500cm以内で試します。攻撃側のグラフはそのまま使えます。
到達点は、最初は満タン、Eを1回押すと80%になり、頭上から「20」が浮かぶことです。元の記事の無敵時間も追加している場合は、1秒以上空けて攻撃します。無敵中はHPを減らさず、新しい数字も追加しません。
新しく作るUIは2つです。
| アセット | 役割 | 表示する場所 |
|---|---|---|
WBP_OverheadHealth | 現在HPの割合を表示 | CubeのWidget Component |
WBP_PracticeDamageNumber | ダメージ量を表示し、上へ動かして消す | ゲーム画面 |
まずバーだけを完成させ、数字は後から加えます。

頭上HPバーを付ける
1. バーだけのWidgetを作る
コンテンツブラウザで「User Interface」→「Widget Blueprint」を作り、親に User Widget を選びます。名前は WBP_OverheadHealth です。
Designerでルートに Size Box、その子に Progress Bar を1つ置きます。初めからCanvas Panelがある場合は削除して、この2つを配置してください。Size Boxは「このUIはこの大きさで表示したい」と指定するための入れ物です。
| 対象 | 設定 |
|---|---|
| Size Box | Width Overrideをオンで 160、Height Overrideをオンで 16 |
| Progress Bar | 名前を HealthFill、「Is Variable」をオン |
| HealthFillのSize Box Slot | Horizontal / Vertical Alignmentを Fill、Paddingを 0 |
| HealthFillのProgress | Percentを 1、Bar Fill Typeを Left to Right。PercentのBindは設定しない |
| HealthFillのAppearance | Fill Color and Opacityを青、不透明にする |
Designerで見える範囲が大きい場合は、プレビューのサイズを「Desired」にすると、バーの大きさを見やすくなります。ここでは、幅160・高さ16のバーが表示されれば準備完了です。

2. Cubeの上に表示場所を作る
BP_OverheadPractice を開き、Componentsで「Add」→「Widget」を選んで OverheadUI と名付けます。DefaultSceneRootの子に置きます。DamageMeshの子にすると、元の記事で設定したメッシュの拡大率も引き継ぐためです。
| OverheadUIの設定 | 値 |
|---|---|
| Location | X=0 / Y=0 / Z=130 |
| Rotation / Scale | 回転はすべて 0、Scaleはすべて 1 |
| Space | Screen |
| Widget Class | None |
| Draw Size | X=160 / Y=16 |
| Draw at Desired Size | オフ |
| Pivot | X=0.5 / Y=0.5 |
| Window Focusable | オフ |
元のCubeは中心から上端まで100cmなので、Z=130なら上端より30cm上です。Pivot は、指定した位置にUIのどこを合わせるか。0.5, 0.5なら、バーの中心をその位置に合わせます。
Widget Classには表示したいクラスを指定する方法もありますが、今回はBeginPlayで中身を作って渡します。そのため None にしておきます。この段階では、まだバーは表示されません。
3. Play開始時に中身を作る
BP_OverheadPracticeのイベントグラフで、元からある Event BeginPlay → Set CurrentHealth の後ろへ、次を追加します。現在HPへMaxHealthを入れる処理は残します。
Create Widgetの Owning Player は、このUIを使うプレイヤーを指定する入力です。今回は1人用なので、Player Indexが0のPlayer Controllerを渡します。

Create WidgetのClassにWBP_OverheadHealth、Owning PlayerにGet Player Controller(Player Index=0)を指定します。- Return Valueを右クリックし「Promote to Variable」で
HealthWidgetと名付けます。型はWBP_OverheadHealth Object Referenceになります。 Get OverheadUIの青いピンからSet Widgetを作り、TargetをOverheadUI、Widget入力をHealthWidgetへつなぎます。白い線はCreate Widget → Set HealthWidget → Set Widgetの順です。
Object Reference(参照) は、作ったWidgetを後から指定するための値です。HealthWidgetへ保存したことで、「どのバーを更新するか」を次の処理でも指定できます。

2枚の図は同じCreate Widgetの入力側と出力側を分けたものです。Create Widgetを2個置く必要はありません。
頭上バーには Add to Viewport を呼びません。Set Widgetで、表示を担当するOverheadUIへ渡しています。コンパイルしてPlayすると、Cubeの上に満タンのバーが現れます。周囲を歩き、Cubeの位置に合って表示されるか確かめましょう。
HPが変わったときにバーを更新する
バーのPercentが受け取るのは、HPそのものではなく 0〜1の割合です。HP80、最大HP100なら 80 ÷ 100 = 0.8 を渡します。
バー側に更新用の関数を作る
WBP_OverheadHealthのGraphで、関数 RefreshOverheadHealth を作ります。入力は Current と Maximum、どちらもFloatです。
入力の値は関数入口のピンから取り出せます。関数内で右クリックし、Get Current、Get Maximum を検索して置く方法でも構いません。
- CurrentをMaximumで割り、
Clamp (Float)のValueへ渡します。Minは0、Maxは1。 - Clampの出力を
Set PercentのIn Percentへつなぎます。 - Set PercentのTargetは、Designerで作った
HealthFillです。関数入口の白い出力をSet Percentの白い入力へつなぎます。

この計算に、実行の順番と表示先をつなぎます。

2枚目のClampは、1枚目と同じノードの出力側です。計算をもう1組作る必要はありません。
今回のMaxHealthは100に固定し、0より大きい値を使います。Clampは結果を範囲内に収めますが、最大HPが0のまま割ってよいわけではありません。
Cubeから、開始時と被弾時に呼ぶ
BP_OverheadPracticeへ戻ります。HealthWidgetをGetで置き、その青いピンから Refresh Overhead Health を検索して呼び出します。TargetはHealthWidget、Currentは Get CurrentHealth、Maximumは Get MaxHealth です。
同じ呼び出しを、次の2か所へ置きます。

| 呼ぶ場所 | つなぐ順番 | 目的 |
|---|---|---|
| BeginPlay | Set Widget → Refresh Overhead Health | 最初のHPをバーへ反映する |
| AnyDamageの処理 | HPを減らすSet CurrentHealth → Refresh Overhead Health → 元のPrint String | 減った後のHPを反映する |
無敵時間を組んでいる場合も、既存のHP減算の後ろに置きます。AnyDamageの直後へ別の線を足すと、無敵でHPが変わらない攻撃にも演出を出してしまいます。元のPrint String以降にある死亡判定と、無敵を解除するTimerは残してください。
Playし直し、Eを1回押してバーが5分の4になれば成功です。ここではまだ数字は出ません。バーは位置には追従しますが、割合を計算するのは開始時とHPが変わったときだけです。
ダメージ数字を浮かべて消す
数字は、出現時に「Cubeの上は、今の画面ではどこか」を調べ、そこへWidgetを置きます。このように 3D空間の位置を画面上の位置へ変え ることを、ここでは 投影 と呼びます。
今回、位置を調べるのは出現時の1回だけです。その後は画面上で上へ動きます。カメラを大きく回すとCubeから離れることもありますが、Cubeが倒れて消えても数字は最後まで表示できます。頭上バーのように、表示中ずっとActorを追う仕組みとはここが違います。

1. 数字のWidgetを作る
WBP_PracticeDamageNumber を作り、ルートをSize Box、その子をText Blockにします。Canvas Panelがある場合は削除します。
| 対象 | 設定 |
|---|---|
| Size Box | Width Override=120、Height Override=80 |
| Text Block | 名前を AmountText、「Is Variable」をオン |
| AmountTextのSize Box Slot | Horizontal / Vertical Alignmentを Center、Paddingを 0 |
| 文字 | Text=20、Font Size=32、色は白、Outline Size=2、Outline Colorは黒 |
| AmountTextのRendering | Render Opacity=1、Clipping=Inherit |
白い数字に黒い縁を付けると、床やCubeが明るくても読みやすくなります。Size BoxもClippingをInheritのままにし、移動す る文字を枠で切り取らないようにします。
2. 上昇とフェードを同時に作る
Designerの「Animations」で「+ Animation」を押し、FloatAndFade と名付けます。パネルが見当たらなければ「Window」から「Animations」と「Timeline」を表示します。
このアニメーションで動かすのはAmountTextです。トラックは「時間に合わせて変える項目」、キーフレームは「この時刻にはこの値にする」という目印です。
FloatAndFadeを選んだ状態でAmountTextを選択し、Detailsの「Render Transform → Translation」と「Render Opacity」の横にあるキー追加ボタンを押します。これで、位置と不透明さを変えるトラックをタイムラインへ追加できます。
タイムラインの表示単位を秒にし、次の2時点へキーを打ちます。フレーム表示を使う場合は、30fpsなら0.6秒が18フレームです。
| 項目 | 0秒のキー | 0.6秒のキー |
|---|---|---|
| Translation X | 0 | 0 |
| Translation Y | 0 | -40 |
| Render Opacity | 1 | 0 |
画面のY座標は下へ行くほど増えるので、上へ動かす値はマイナスです。Render Opacityは見た目の不透明さで、1なら見え、0なら透明になります。
各項目の値を変えるだけでなく、キーが置かれたことを確認してください。再生範囲の終端も0.6秒に揃えます。Designerで再生すると、「20」が上へ40だけ動きながら薄くなれば準備完了です。位置 の単位はUIの表示倍率で拡大・縮小されるため、ここでの40は常に物理的な40ピクセルという意味ではありません。

図の0.6秒にある点線の数字は、移動先を示す目印です。実際の表示では完全に透明になります。
3. 数値を受け取り、終了したら片付ける
WBP_PracticeDamageNumberの イベントグラフ に、カスタムイベント StartDamagePopup を作ります。入力 Amount はFloatにします。

- Amountを
To Text (Float)へ渡します。詳細ピンのMinimum Fractional DigitsとMaximum Fractional Digitsをどちらも0にし、今回は整数の見た目にします。 Set Text (Text)のTargetへAmountText、In Textへ変換した値をつなぎます。イベントの白い出力をSet Textへつなぎます。- 続けて
Play Animation with Finished eventを置きます。右クリックでGet a reference to selfを検索し、その青い出力をWidgetへつなぎます。Selfは、この数字のWidget自身です。In AnimationにはGet FloatAndFadeをつなぎ、Start at Time=0、Num Loops to Play=1、Play Mode=Forward、Playback Speed=1にします。 - このノードの Finished から
Remove from Parent(TargetはSelf)を呼びます。通常の白い実行出力は空けます。
FloatAndFadeは、アニメーションを作ったときに用意される変数です。My Blueprintの「Animations」から、Ctrl を押しながらグラフへドラッグするとGetとして置けます。

Finishedは、アニメーションが終わったときに進む出力です。再生直後に進む通常の出力へRemove from Parentをつなぐと、数字がすぐに消えてしまいます。また、この再生ノードは関数内には置けないので、カスタムイベントを使っています。
透明にするだけでは、画面に追加したWidgetは残ります。Remove from Parentまでつないで、今回の表示を終えます。
4. Cubeの頭上を、画面の座標へ変える
BP_OverheadPracticeのイベントグラフへ、カスタムイベント ShowPracticeDamageNumber を作り、Float入力 Amount を追加します。
Project World Location to Widget Position を置き、次の値をつなぎます。このノードには白い実行ピンがなく、接続先が値を使うときに結果を求めます。
| 入力 | 値 |
|---|---|
| Player Controller | Get Player Controller(Player Index=0) |
| World Location | Get OverheadUI → Get World Location |
| Player Viewport Relative | オフ |
ここで使うのは、Cube本体の中心ではなく OverheadUIのワールド位置です。ワールド位置は、レベル全体を基準にした位置。先ほど設定した相対位置Z=130とは異なり、Cubeが置かれた場所も含んでいます。

イベントの白い出力を Branch へつなぎ、投影ノードのReturn ValueをConditionに渡します。True側だけで、次の数字生成へ進みます。False側には何もつなぎません。カメラの後方など、位置を投影できなかった場合に表示を始めないためです。ただし、Trueでも画面の外側の座標になる場合はあります。
