【Unreal Engine】UE5の頭上HPバーとダメージ数字:Actorに付くUIと、浮かんで消えるUI

作成: 2026-07-23最終更新: 2026-09-06

Eキーで攻撃するCubeにHPバーを付け、20ダメージの数字を浮かせます。Widget Componentの入れ物と中身、ScreenとWorld、座標の変換、UMGアニメーションの終了までを順に解説。初期表示やDPIによる位置ずれも確認します。

攻撃が当たってHPは減っている。それでも、画面の隅に数値が出るだけでは「どの相手に、どれだけ効いたか」が分かりにくいものです。相手の頭上にバーが付き、攻撃のたびに「20」が浮かべば、手応えをその場で確かめられます。

この記事では、体力とダメージの記事で作ったCubeに、頭上HPバーとダメージ数字を足します。バーはCubeについて動き、数字は出現した画面位置からふわっと上がって消える。表示を付ける場所と、表示を終えるタイミングを分けて考えるのがポイントです。

CubeのHPが満タンから80%へ減り、20の数字が出て0.6秒後に消える完成イメージ

図のバーは割合を見比べるため5区画で表しています。実践で作るのは、区切りのない1本のバーです。

この記事でわかること

  • Widget Componentで、Actorの頭上にHPバーを付ける方法
  • ScreenとWorldの見え方、入れ物と中身の違い
  • 3Dの位置を画面の位置へ変え、ダメージ数字を出す手順
  • 数字を上へ動かして薄くし、アニメーション終了後に片付ける方法

Sponsored

画面に置く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本体 に対して呼びます。

Widget Componentの中にUser Widgetがあり、さらにその中にProgress Bar本体がある関係

Get User Widget Object は、Widget Componentが表示している中身のWidgetを取り出すノードです。そこから Cast To WBP_OverheadHealth を使うと、その種類のWidgetに用意した関数を呼べます。Castは新しいバーを作る操作ではありません。 すでにある中身を、目的の型として扱うための確認です。

実践では、最初にWidgetを作ったときの戻り値を保存します。これで「今表示しているバー」を直接指定できるため、更新のたびに取り出し直す必要がありません。

ScreenとWorld:読みやすさと、世界の中の見え方

Widget Componentの Space は、UIをどこに描くかを決める設定です。同じActorに付いていても、見え方が変わります。

Screenの頭上UIは壁によって自動では隠れず、Worldのパネルは3D空間の面として手前の壁に隠れる比較
Space見え方使いどころ
ScreenActorの位置をもとに画面上へ表示。カメラを回しても文字は傾かない頭上HPバー、名前
World3D空間に置いた面として表示。遠ざかると小さくなり、斜めから見ると傾く壁の操作パネル、端末の画面

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ダメージ量を表示し、上へ動かして消すゲーム画面

まずバーだけを完成させ、数字は後から加えます。

到達点は80%のバーと浮かぶ20。バーはCubeのWidget Component、数字はゲーム画面に出す

頭上HPバーを付ける

1. バーだけのWidgetを作る

コンテンツブラウザで「User Interface」→「Widget Blueprint」を作り、親に User Widget を選びます。名前は WBP_OverheadHealth です。

Designerでルートに Size Box、その子に Progress Bar を1つ置きます。初めからCanvas Panelがある場合は削除して、この2つを配置してください。Size Boxは「このUIはこの大きさで表示したい」と指定するための入れ物です。

対象設定
Size BoxWidth Overrideをオンで 160、Height Overrideをオンで 16
Progress Bar名前を HealthFill、「Is Variable」をオン
HealthFillのSize Box SlotHorizontal / Vertical Alignmentを Fill、Paddingを 0
HealthFillのProgressPercentを 1、Bar Fill Typeを Left to Right。PercentのBindは設定しない
HealthFillのAppearanceFill Color and Opacityを青、不透明にする

Designerで見える範囲が大きい場合は、プレビューのサイズを「Desired」にすると、バーの大きさを見やすくなります。ここでは、幅160・高さ16のバーが表示されれば準備完了です。

Size Boxの子にHealthFillを置き、ActorではDamageMeshとOverheadUIをDefaultSceneRootの同じ階層の子にする構成

2. Cubeの上に表示場所を作る

BP_OverheadPractice を開き、Componentsで「Add」→「Widget」を選んで OverheadUI と名付けます。DefaultSceneRootの子に置きます。DamageMeshの子にすると、元の記事で設定したメッシュの拡大率も引き継ぐためです。

OverheadUIの設定
LocationX=0 / Y=0 / Z=130
Rotation / Scale回転はすべて 0、Scaleはすべて 1
SpaceScreen
Widget ClassNone
Draw SizeX=160 / Y=16
Draw at Desired Sizeオフ
PivotX=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を渡します。

HP初期化後にCreate Widgetを実行し、Player ControllerをOwning Playerへつなぐ入力側の接続
  1. Create Widget のClassに WBP_OverheadHealth、Owning Playerに Get Player Controller(Player Index=0)を指定します。
  2. Return Valueを右クリックし「Promote to Variable」で HealthWidget と名付けます。型は WBP_OverheadHealth Object Reference になります。
  3. Get OverheadUI の青いピンから Set Widget を作り、TargetをOverheadUI、Widget入力をHealthWidgetへつなぎます。白い線は Create Widget → Set HealthWidget → Set Widget の順です。

Object Reference(参照) は、作ったWidgetを後から指定するための値です。HealthWidgetへ保存したことで、「どのバーを更新するか」を次の処理でも指定できます。

Create Widgetの戻り値をHealthWidgetに保存し、OverheadUIのSet Widgetへ渡す接続

2枚の図は同じCreate Widgetの入力側と出力側を分けたものです。Create Widgetを2個置く必要はありません。

頭上バーには Add to Viewport を呼びません。Set Widgetで、表示を担当するOverheadUIへ渡しています。コンパイルしてPlayすると、Cubeの上に満タンのバーが現れます。周囲を歩き、Cubeの位置に合って表示されるか確かめましょう。

Sponsored

HPが変わったときにバーを更新する

バーのPercentが受け取るのは、HPそのものではなく 0〜1の割合です。HP80、最大HP100なら 80 ÷ 100 = 0.8 を渡します。

バー側に更新用の関数を作る

WBP_OverheadHealthのGraphで、関数 RefreshOverheadHealth を作ります。入力は CurrentMaximum、どちらもFloatです。

入力の値は関数入口のピンから取り出せます。関数内で右クリックし、Get CurrentGet Maximum を検索して置く方法でも構いません。

  • CurrentをMaximumで割り、Clamp (Float) のValueへ渡します。Minは 0、Maxは 1
  • Clampの出力を Set Percent のIn Percentへつなぎます。
  • Set PercentのTargetは、Designerで作った HealthFill です。関数入口の白い出力をSet Percentの白い入力へつなぎます。
Currentを割り算のA、MaximumをBへ渡し、結果を0から1へClampする計算の接続

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

関数の実行線、HealthFillの参照、Clampの計算結果を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か所へ置きます。

HealthWidgetを更新先にし、現在HPと最大HPをRefresh Overhead Healthへ渡す接続
呼ぶ場所つなぐ順番目的
BeginPlaySet 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を追う仕組みとはここが違います。

Cubeの頭上の3D位置を画面上の位置へ変え、その場所から数字を上昇させる仕組み

1. 数字のWidgetを作る

WBP_PracticeDamageNumber を作り、ルートをSize Box、その子をText Blockにします。Canvas Panelがある場合は削除します。

対象設定
Size BoxWidth Override=120、Height Override=80
Text Block名前を AmountText、「Is Variable」をオン
AmountTextのSize Box SlotHorizontal / Vertical Alignmentを Center、Paddingを 0
文字Text=20、Font Size=32、色は白、Outline Size=2、Outline Colorは黒
AmountTextのRenderingRender 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 X00
Translation Y0-40
Render Opacity10

画面のY座標は下へ行くほど増えるので、上へ動かす値はマイナスです。Render Opacityは見た目の不透明さで、1なら見え、0なら透明になります。

各項目の値を変えるだけでなく、キーが置かれたことを確認してください。再生範囲の終端も0.6秒に揃えます。Designerで再生すると、「20」が上へ40だけ動きながら薄くなれば準備完了です。位置の単位はUIの表示倍率で拡大・縮小されるため、ここでの40は常に物理的な40ピクセルという意味ではありません。

0秒から0.6秒の間に数字を上へ40動かし、不透明さを1から0へ変えるアニメーション

図の0.6秒にある点線の数字は、移動先を示す目印です。実際の表示では完全に透明になります。

3. 数値を受け取り、終了したら片付ける

WBP_PracticeDamageNumberの イベントグラフ に、カスタムイベント StartDamagePopup を作ります。入力 Amount はFloatにします。

AmountをTo Textで文字へ変え、AmountTextのSet Textへ渡す接続
  1. Amountを To Text (Float) へ渡します。詳細ピンのMinimum Fractional DigitsとMaximum Fractional Digitsをどちらも 0 にし、今回は整数の見た目にします。
  2. Set Text (Text) のTargetへAmountText、In Textへ変換した値をつなぎます。イベントの白い出力をSet Textへつなぎます。
  3. 続けて 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 にします。
  4. このノードの Finished から Remove from Parent(TargetはSelf)を呼びます。通常の白い実行出力は空けます。

FloatAndFadeは、アニメーションを作ったときに用意される変数です。My Blueprintの「Animations」から、Ctrl を押しながらグラフへドラッグするとGetとして置けます。

数字のWidget自身とFloatAndFadeを再生ノードへ渡し、FinishedからRemove from Parentを呼ぶ接続

Finishedは、アニメーションが終わったときに進む出力です。再生直後に進む通常の出力へRemove from Parentをつなぐと、数字がすぐに消えてしまいます。また、この再生ノードは関数内には置けないので、カスタムイベントを使っています。

透明にするだけでは、画面に追加したWidgetは残ります。Remove from Parentまでつないで、今回の表示を終えます。

Sponsored

4. Cubeの頭上を、画面の座標へ変える

BP_OverheadPracticeのイベントグラフへ、カスタムイベント ShowPracticeDamageNumber を作り、Float入力 Amount を追加します。

Project World Location to Widget Position を置き、次の値をつなぎます。このノードには白い実行ピンがなく、接続先が値を使うときに結果を求めます。

入力
Player ControllerGet Player Controller(Player Index=0
World LocationGet OverheadUI → Get World Location
Player Viewport Relativeオフ

ここで使うのは、Cube本体の中心ではなく OverheadUIのワールド位置です。ワールド位置は、レベル全体を基準にした位置。先ほど設定した相対位置Z=130とは異なり、Cubeが置かれた場所も含んでいます。

OverheadUIのワールド位置とPlayer Controllerを投影ノードへ渡す接続

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

投影ノードのReturn ValueをBranchのConditionへつなぎ、True側だけで数字の生�成へ進む接続

この図の投影ノードは、直前の図と同じものです。ここでは、成功したかを表す赤い出力だけを取り出して示しています。

Event AnyDamage には、実際に攻撃が当たった位置の出力はありません。今回の数字は Cubeの頭上 へ出します。剣先や着弾点へ出したい場合は、Point Damageなどで命中位置を別途渡す段階になります。

5. 数字を作り、その位置で再生する

BranchのTrueから Create Widget を呼びます。ClassはWBP_PracticeDamageNumber、Owning Playerは先ほどと同じPlayer Controllerです。バーを作ったときと同じく、Return Valueを右クリックして「Promote to Variable」を選び、PopupWidget と名付けます。型は WBP_PracticeDamageNumber Object Reference です。

そこから、次の順に白い線をつなぎます。各ノードのTargetは、すべてPopupWidgetです。

順番ノード渡す値
1Add to ViewportZOrder=10
2Set Desired Size in ViewportSize=X:120 / Y:80
3Set Alignment in ViewportAlignment=X:0.5 / Y:0.5
4Set Position in ViewportPosition=投影ノードのScreen Position、Remove DPI Scaleはオフ
5Start Damage PopupAmount=ShowPracticeDamageNumberのAmount
PopupWidgetを画面へ追加し、表示サイズを120×80に設定する接続

ZOrderは、画面に重なるWidgetの前後関係です。値が大きいものほど手前になります。サイズを決め、Alignmentで中央を基準にしてから位置を渡すので、数字の中心を頭上へ合わせられます。ここは分割画面を使わない1人用の手順です。

PopupWidgetのSet Position in Viewportへ投影済みのScreen Positionを渡し、Remove DPI Scaleをオフにする接続

Remove DPI Scaleをオフにする理由も押さえておきましょう。UIは画面サイズに合わせて拡大・縮小されます。この表示倍率を考慮する仕組みがDPIスケーリングです。今回は投影ノードがすでに補正した座標を返すため、Set Position in Viewportで同じ補正を重ねないようにします。

位置を決めたら、PopupWidgetの青い出力から Start Damage Popup を検索します。Amountへ渡すのは、このイベントが受け取ったAmountです。これで、先ほど数字側に作った「文字を変えて、上昇させて、片付ける」処理が始まります。

PopupWidgetをTargetにし、ShowPracticeDamageNumberのAmountをStart Damage Popupへ渡す接続

最後に、HPを減らした後の流れへ呼び出しを追加します。

HPバー更新の実行出力を数字表示へつなぎ、AnyDamageのDamageをAmountへ渡す接続
BP_OverheadPractice:既存の被弾処理の続き
Set CurrentHealth
  → Refresh Overhead Health(Target: HealthWidget)
  → Show Practice Damage Number(Target: Self、Amount: AnyDamageのDamage)
  → 元のPrint String
  → 元の死亡判定・Timer

HPを減らす箇所は増やしません。表示処理を、更新後のHPと受け取ったダメージ量につないでいます。今回の20という数字は、受け付けた攻撃のダメージ量です。

動かして確かめる

コンパイルし、Playし直してゲーム画面をクリックします。元の記事と同じく、Cubeへキャラクターを向けてEを押してください。

操作確かめる結果
攻撃せずに周囲を歩くバーは満タンで、Cubeの上に表示される
Eを1回押すバーが80%になり、「20」が上がって0.6秒で消える
無敵時間より短い間隔で連打する無敵を組んでいる場合、HPも数字の追加も止まる
間隔を空け、合計5回当てるCubeと頭上バーが消え、最後の数字は上がって消える
数字が出た直後にカメラを大きく回すバーはCubeの位置に合う。数字は出現時の画面位置から上がる
ゲーム画面の大きさを変えて試す数字の出現位置が頭上から大きくずれない

5回目の数字が最後まで動くのは、画面へ追加したWidget自身が再生と片付けを担当しているためです。CubeのActorを消す処理とは分かれています。

攻撃前・1回目・5回目の見え方。箱が消えても、出た数字は最後まで上がって消える

うまくいかないとき

症状確認する場所
最初からバーが出ないBeginPlayのCreate Widget、Set HealthWidget、Set Widgetの実行線。Set WidgetのTargetがOverheadUIか
最初から空のバーになる元のBeginPlayでCurrentHealthへMaxHealthを入れたか。初回のRefreshがその後か
HPのPrintは減るのにバーが変わらない呼び出しのTargetがHealthWidgetか。関数内のSet PercentのTargetがHealthFillか
バーは減るが数字が出ないShowPracticeDamageNumberを呼んだか。投影成功のTrue側からCreate Widgetへつながるか
数字が一瞬で消えるRemove from Parentが、再生ノードのFinishedにつながるか
数字が動かない・消えないFloatAndFadeを指定したか。両時刻のキー、再生範囲、ループ回数が合っているか
画面サイズによって位置がずれるRemove DPI Scaleがオフか。Desired SizeとAlignmentを設定したか

値を確かめたいときは、Print Stringで投影のReturn Valueや、表示へ渡すAmountを一時的に出します。バーと数字を別々に確かめると、問題の場所を絞れます。

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

表示中ずっと追わせたい場合。 今回の数字は出現位置を1回だけ求めています。移動中も同じ3D地点に重ねたいなら、その地点を保存して表示中に再投影するなど、位置を更新する処理が必要です。敵そのものを追う場合は、敵が消えた後の扱いも決めます。

HPを減らす処理と、表示を更新する処理。 今回はActorからWidgetの関数を直接呼びました。同じHPを画面のHUDにも出すなど、通知先が増えたらEvent Dispatcherで「HPが変わった」と知らせる形へ広げられます。開始時の表示を合わせることは、どちらの方法でも必要です。

数が増えたときの見直し。 今回も被弾のたびにCreate Widgetしています。画面へ出す方式にしただけで、大量表示が軽くなるとは限りません。まず同時に出す個数や、遠い敵のバーを表示する必要があるかを見直し、UIの負荷を調べる手順で確かめます。使い回す仕組みを加えるなら、文字、位置、透明度、アニメーションの再生状態も毎回戻します。

数字に意味を足す。 クリティカルだけ色や大きさを変えると、同じダメージ表示でも攻撃の違いが伝わります。防御力を追加したら、表示する量も防御計算後へ揃えましょう。「受け付けたダメージ量」と「残りHPから実際に減った量」のどちらを見せるかも、ゲームのルールに合わせて決めます。

まとめ

頭上HPバーは、Widget Componentに中身のWidgetを渡し、HPが変わったときに割合を更新します。ダメージ数字は、頭上の位置を画面の座標へ変えて表示し、Widget自身のアニメーションが終わったら片付けます。

まずは20ダメージでバーが減るところを確かめ、その後で数字の上昇量や0.6秒の長さを変えてみてください。HPを減らす処理はそのままに、攻撃の手応えだけを調整できるようになります。

参考:UMGのアニメーションWidget ComponentScreenとWorldWidget用の座標投影Set Position in ViewportPlay Animation with Finished event

Unreal Engine このセクションのノート98