【Unreal Engine】UE5の体力とダメージ入門:Apply DamageとEvent AnyDamageで攻撃を当てる

作成: 2026-07-20最終更新: 2026-09-05

Eキーで目の前のCubeに20ダメージを与え、HPが0になったら消す仕組みを作ります。Apply Damageで伝える側とEvent AnyDamageでHPを減らす側を分け、被弾後1秒の無敵時間も追加。Point・Radial Damageの使い分けや、攻撃者と発生源の違いも図解します。

剣が当たったら敵のHPを減らす。壊せる箱を撃ったら、その耐久値を減らす。どちらも似た処理ですが、攻撃する側に相手ごとのHP計算を書いていると、無敵時間や防御力を足すときに直す場所が増えてしまいます。

UEの Apply Damage を使うと、攻撃側は「この相手に20ダメージ」と伝え、受け側が自分のルールでHPを減らせます。この記事では、まず正面のCubeを5回の攻撃で倒す仕組みを作り、連打してもすぐには次のダメージが入らない無敵時間を加えます。

相手を調べ、Apply Damageで20を伝え、受け側が自作のHP処理で100から80へ減らす3段階

この記事でわかること

  • ダメージを伝える処理と、HPを減らす処理の役割分担
  • Eキーで攻撃し、HP100を20ずつ減らして倒す手順
  • 被弾後1秒だけ、追加のダメージを受け付けない仕組み
  • Point・Radial Damageの使い分けと、攻撃者・発生源の渡し方

Sponsored

Apply Damageは「ダメージを伝える入口」

Actor は、レベルに配置した敵や箱など、一つひとつの物を表します。Apply Damage は、そのActorを指定してダメージ量を渡すノードです。受け側のBlueprintでは、Event AnyDamage から通知を受けた後の処理を始めます。

たとえば20ダメージを伝えたとき、普通の敵はHP100から80へ、防御中の敵は半分の10だけ減らして90へ、といったルールを受け側に書けます。攻撃側が相手のHP変数を探して書き換える必要はありません。

ここで大切なのは、Apply Damageを呼ぶだけでは、HPも死亡処理も用意されない ことです。HPは自分で作る変数です。「通知を受け取る → HPを減らす → 0なら倒す」という続きも、自分でつなぎます。

Apply Damageは量を渡すだけで、HPを減らすか倒すかは受け側が決��める

相手のHPを直接書き換える方法でも、小さな試作は作れます。ただ、剣にも弾にも「相手が無敵なら減らさない」と書くより、受け側の入口でまとめて判断する方が、ルールを変更しやすくなります。Blueprint Interfaceで共通の呼び出し方を作るのと近い考え方です。

実践:正面のCubeを5回で倒す

Third PersonテンプレートのBlueprintプロジェクト を使います。新規作成時に「Variant」がある版では None を選びます。変数を作り、ノードのピンをつなぐ操作が分かれば進められます。

E キーを1回押すと、キャラクターの正面にいるCubeへ20ダメージを送ります。画面には現在のHPが 80 → 60 → 40 → 20 → 0 と表示され、5回目に Destroyed と出てCubeが消えます。まずは押すたびに減るところまで作り、次の節で無敵時間を足しましょう。

Eキーで正面のCubeへ攻撃し、5回の命中でHP100から0になって消える完成イメージ

1. ダメージを受けるCubeを用意する

  1. Actorを親にBlueprint Classを作り、BP_Damageable と名付けます。
  2. Static Meshコンポーネントを追加して DamageMesh と名付け、標準の CubeEngine/BasicShapes)を割り当てます。見つからない場合はアセット選択欄でエンジンコンテンツの表示をオンにします。DefaultSceneRoot の子のままで構いません。
  3. DamageMeshの位置・回転をすべて 0、Scaleを X=1.5 / Y=1.5 / Z=2 にします。「Simulate Physics」はオフ、「Collision Presets」は BlockAll にします。
  4. 「Class Defaults」で「Can Be Damaged」がオンであることを確認します。これは、Actorが標準のダメージを受けられるかを決める設定です。
  5. レベルに1体置き、Actor自体のScaleは 1, 1, 1 にします。Cubeは高さ200cmなので、Actorの中心を床から100cm上げると底が床に揃います。

プレイヤーの開始位置から300cmほど離れた、歩いて正面へ回り込める場所に置いてください。今回の攻撃判定はキャラクターの中心の高さを通ります。Cubeを縦に大きくしたのは、その線を当てやすくするためです。

BP_Damageableに次の変数を作り、コンパイルして既定値を設定します。Float は小数を扱える数値の型です。

変数既定値今回の役割
MaxHealthFloat100最大HP
CurrentHealthFloat0現在のHP

イベントグラフに Event BeginPlay → Set CurrentHealth をつなぎ、値には Get MaxHealth を渡します。これでPlayするたびに、現在HPが最大HPの100から始まります。

2. キャラクターの正面に攻撃判定を出す

BP_ThirdPersonCharacter のイベントグラフを開き、キーボードの E イベントと Line Trace By Channel を追加します。EPressed から、白い実行線をTraceへつなぎます。

Line Trace は、見えない線を引き、その線を遮った最初の物を調べる機能です。今回はキャラクターの位置から正面500cmまでを調べます。アニメーションや剣のモデルは使わず、この線を攻撃判定にします。

線の始点と終点は、次のように組みます。

Traceの入力つなぐ値
StartGet Actor Location(TargetはSelf)
EndGet Actor Location + Get Actor Forward Vector × 500

Get Actor Forward Vector は「このキャラクターが向いている方向」を表す、長さ1の矢印です。500倍すると長さ500cmになり、それを現在位置に足すと線の終点が求まります。掛け算は Vector × Float、足し算は Vector + Vector にします。掛け算の両入力がVectorになった場合は、500を入れる側のピンを右クリックしてFloatへ変換します。

Actorの現在位置に正面方向の500倍を足し、Traceの終点を求めるVectorの接続

Traceの設定は次に揃えます。

設定
Trace ChannelVisibility
Trace Complexオフ
Ignore Selfオン
Draw Debug TypeFor Duration
Draw Time1.0

Visibility は、今回「何が線を遮るか」を決める判定の種類です。先ほどの BlockAll はこの判定を遮る設定なので、Cubeに線が当たります。Ignore Self は自分自身を判定から除くために使います。

EのPressedからTraceを実行し、現在位置をStart、前の図の計算結果をEndへ渡す接続と設定

図を2枚に分けています。前の図の + の出力を、この図の End へつなぎます。Get Actor Locationは、同じノードからStartと足し算へ線を分けても構いません。

ここで一度コンパイルしてPlayし、ゲーム画面をクリックしてから E を押します。1秒間表示される線が、Cubeへ届くか見てください。カメラだけを回しても、キャラクターの正面は変わらない場合があります。 Cubeの方へ少し歩いてキャラクターを向け、500cm以内で試します。

3. 当たったActorへ20ダメージを送る

Traceの出力を使い、次のようにつなぎます。

  1. 白い実行出力を Branch へ、赤い Return Value をその Condition へつなぎます。このBoolean値は、線を遮る物が見つかると true になります。
  2. Out Hit から Break Hit Result を作ります。Hit Result は、当たったActorや位置をまとめた結果です。Break で中身を取り出します。
  3. Branchの True から Apply Damage を呼びます。False は何もつなぎません。

Apply Damage の入力は次のとおりです。

Traceの命中結果でBranchを分け、Hit Actorへ20ダメージを送り、ControllerとSelfを別の入力へ渡す接続

図の左端は、前の手順で作ったTraceの出力側です。新しいTraceをもう1つ追加する必要はありません。

入力今回の値意味
Damaged ActorBreak Hit Resultの Hit Actor当たった相手
Base Damage20相手へ伝えるダメージ量
Event InstigatorGet Controller(TargetはSelf)攻撃したキャラクターのController
Damage CauserSelf今回の攻撃元であるキャラクター
Damage Type ClassDamageType標準のダメージの種類

Self は「今このグラフを動かしているActor自身」です。このグラフではプレイヤーキャラクターを指します。ControllerとDamage Causerの違いは、実践の後で詳しく整理します。

ここまでで、当たった相手へダメージを伝える準備ができました。まだCubeは消えません。次に、受け取った後の処理を作ります。

4. 受け側でHPを減らし、画面へ出す

BP_Damageable に戻り、イベントグラフへ Event AnyDamage を追加します。レベルのBlueprintではなく、ダメージを受けるCubeのBlueprint に置きます。

Event AnyDamage の白い出力から Set CurrentHealth を呼びます。代入する値は、次の順で計算します。

  1. Floatの引き算を置き、上の入力へ Get CurrentHealth、下の入力へイベントの Damage をつなぎます。計算するのは 現在HP − 受けたダメージ です。
  2. 結果を Clamp (Float)Value へ渡します。Min0MaxGet MaxHealth です。
  3. Clampの出力を Set CurrentHealth の値へつなぎます。

Clamp は、数値を指定した範囲に収めるノードです。HP10の相手へ20ダメージを与えても、結果をマイナス10ではなく0にできます。

CurrentHealthからAnyDamageのDamageを引き、Clampで0からMaxHealthに収めてCurrentHealthへ代入する接続

図の左から来る白い線とDamageの緑の線は、どちらも作成したEvent AnyDamageからつなぎます。

続けて Print String を置き、Set CurrentHealthの白い出力からつなぎます。Get CurrentHealthIn String へ接続すると、数値を文字列にする変換ノードが入ります。「Print to Screen」をオン、Duration10 にしてください。

上段はBeginPlayでMaxHealthをCurrentHealthへ入れる初期化、下段はダメージで減らした現在HPをPrint Stringへ渡す接続

上段は手順1の初期化、下段は今作っている表示で、別々の流れです。下段のPrintへは、ダメージを受けてHPを減らしたSet CurrentHealthから白い線をつなぎます。

この段階でPlayし直して E を1回押すと、画面に 80(表示形式によっては 80.0)が出ます。これが、通知を受けたCube自身がHPを減らした結果です。

5. HPが0ならCubeを消す

Print Stringの後ろに Branch を足し、Get CurrentHealth <= 0 をConditionへつなぎます。True から Print String(文字は Destroyed、Durationは 10)、続けて Destroy Actor(TargetはSelf)を呼びます。False は今の段階では空けておきます。

HPを表示した後�で現在HPと0を比較し、0以下ならBranchのTrue側へ進める接続

このBranchの True を、次の図のPrint Stringの白い入力へつなぎます。HPの数値を出すPrintと、Destroyed を出すPrintは別のノードです。

BranchのTrueからDestroyedを表示し、その後にDestroy ActorでCube自身を消す接続

コンパイルしてPlayし直し、Cubeへ向けて E を5回押します。各回でキーを離して押し直してください。押しっぱなしではPressedは繰り返されません。

命中した回数表示されるHPCube
1回80残る
2回60残る
3回40残る
4回20残る
5回0、その後Destroyed消える

初期値100は、BeginPlayでPrintしていないので画面には出ません。0になった後もCubeが残るなら、死亡判定のBranchからTrue側の接続を確認します。

Sponsored

無敵時間:被弾後1秒はHPを減らさない

今のCubeは、短い間に E を何度も押すと、その回数だけHPが減ります。これに「一度ダメージを受けたら、1秒間は次を受け付けない」というルールを加えます。これが今回の 無敵時間 です。

攻撃する側は引き続きApply Damageを呼びます。受け側が通知を受けても、無敵中ならHPを減らす手前で処理を終えます。

無敵中かどうかを記録する

BP_Damageableに2つの変数を追加します。

変数既定値役割
IsInvincibleBooleanfalse無敵中ならtrue
InvincibleTimeFloat1.0無敵が続く秒数

Event AnyDamageとSet CurrentHealthの間に、次の処理を挟みます。

  1. Event AnyDamageの白い出力を Branch へつなぎ、Conditionへ Get IsInvincible を渡します。
  2. True は空けます。無敵中なので、ここで終わります。
  3. False から Set IsInvincible を呼び、値を true にします。
  4. その白い出力を、作成済みの Set CurrentHealth へつなぎ直します。Damageから引き算へ渡している数値の線は、そのまま使います。

これで一度受けた直後から、次の通知を止められます。ただし、このままではずっと無敵なので、時間が来たら解除する処理も作ります。

IsInvincibleがtrueなら終了し、falseなら無敵を有効にしてから既存のHP計算へ進める接続

Timerで1秒後に解除する

Timer は、指定した時間が経ったらイベントを呼ぶ仕組みです。BP_Damageableにカスタムイベント EndInvincible を作り、その白い出力から Set IsInvincible(値は false)を呼びます。

次に、HPが0かを調べているBranchの、空けていた False側Set Timer by Event をつなぎます。「まだ生きているときだけ、無敵を解除するタイマーを動かす」という順です。

  • Event:作った EndInvincible の赤い四角の出力をつなぐ
  • TimeGet InvincibleTime
  • Looping:オフ
生存時にSet Timer by Eventを呼び、赤い線で指定したEndInvincibleが時間経過後にIsInvincibleをfalseへ戻す接続

赤い線は「時間が来たら、このイベントを呼ぶ」という指定です。白い線とは役割が違います。Looping をオフにすると、1秒後に1回だけ呼ばれます。Timerの戻り値は今回は使いません。

InvincibleTime は0より大きい値にしてください。このノードへ0以下を渡しても、即座に解除イベントが動くわけではありません。

連打と、時間を空けた攻撃を比べる

コンパイルしてから、ケースごとにPlayをやり直します。現在HP100、無敵ではない状態から確認するためです。

操作期待する結果
Cubeへ1回命中させ、その直後の1秒以内に追加で押す最初の80だけ表示され、追加ではHPが減らない
1回命中させ、1秒より長く待ってからもう1回80、60と減る
毎回1秒より長く空けて、合計5回命中させる80→60→40→20→0となり、Destroyedと出て消える

次は InvincibleTime2.0 に変えてみましょう。Playし直すと、同じ連打でもHPが減る間隔が長くなります。確認後は 1.0 に戻します。

ダメージ量も試せます。攻撃側の Base Damage を40にしてPlayし直すと、十分な間隔を空けた3回の命中で 60 → 20 → 0 になります。最後がマイナス20にならないのはClampの働きです。確認後は20へ戻してください。

Cubeを複製して横に並べると、それぞれが別のHPと無敵時間を持ちます。向きを変えて1体ずつ狙えます。今回のLine Traceは最初に線を遮った1体が対象なので、1回で奥のCubeまでまとめて攻撃するものではありません。

うまく動かないとき

症状確認する場所
Eを押しても線が出ないゲーム画面をクリックしたか。Eイベントを操作中のBP_ThirdPersonCharacterに置いたか
線がCubeへ届かないキャラクターの向き、500cmの距離、線とCubeの高さ。TraceのEndが「位置+方向×500」か
線が当たるのにHPが出ないDamageMeshがVisibilityをBlockするか。Can Be Damagedがオンか。AnyDamageをBP_Damageableに置いたか
1回目から0になるBeginPlayでCurrentHealthへMaxHealthを入れたか。減算が「CurrentHealth − Damage」か
2回目以降、ずっと減らないTimerの赤いEvent接続、Timeが正の値か、EndInvincibleでfalseへ戻しているか
無敵中もHPが減るAnyDamage直後のBranchで、True側を空け、False側だけHP計算へ進めているか

線を目で確認する方法はLine Traceの記事、値の確認はPrint Stringの記事でも扱っています。

Sponsored

攻撃に合わせて3つのノードを使い分ける

ここまでは、相手1体とダメージ量を指定するApply Damageを使いました。銃撃や爆発では、追加で渡したい情報や、相手の探し方が変わります。

指定した1体へのDamage、命中位置も渡すPoint、中心と半径や遮蔽を使うRadialの用途比較
ノード対象の決め方向いている場面
Apply Damage自分で指定した1体接触ダメージ、毒など、位置の情報が要らない攻撃
Apply Point Damage自分で指定した1体に、命中の位置や方向も渡す銃撃の着弾位置にエフェクトを出す
Apply Radial Damage中心と半径から、条件を満たす対象を調べる爆発や範囲魔法

Apply Point Damage自体は、命中判定をしません。 今回と同じようにTraceなどで相手を調べ、そのHit Resultを Hit Info へ、攻撃が進む向きを Hit from Direction へ渡します。

一方、Apply Radial Damage は、爆心地の Origin と半径の Damage Radius を使って対象を調べます。範囲内なら何でも無条件に当たるわけではなく、対象のコリジョンや遮る物も関係します。

標準の処理では、VisibilityをBlockするComponentが対象になります。また、Damage Prevention Channel は爆心地から対象への遮蔽判定に使う種類で、既定値はVisibilityです。その判定をBlockする壁が間にあれば、爆風を遮れます。爆発が当たらないときは、半径に加えてコリジョンの設定も確認します。

HPを減らす場所は重ねない

受け側には、Event PointDamageEvent RadialDamage もあります。Pointには命中位置など、Radialには爆心地など、その攻撃に特有の情報が届きます。

ただし、PointやRadialのダメージでは Event AnyDamageも呼ばれます。AnyDamageとPointDamageの両方に「HPを20減らす」と書けば、1回の銃撃で合計40減る原因になります。

2つのイベントで減算すると合計40減り、AnyDamageだけで減算すれば20になる役割分担の概念図

今回のHP処理を使い回すなら、減算はAnyDamageに置き、PointDamageは着弾エフェクトなどに分けます。部位によってダメージ量を変えたいときも、最終的にHPを減らす処理が1回だけになるように組みましょう。

Damage CauserとInstigatorは何が違う?

弾が敵に当たったとき、「どの弾からのダメージか」と「誰が撃ったのか」は別の情報です。前者が Damage Causer、後者をControllerで表したものが Event Instigator です。

Controller は、キャラクターを操作する側を表すオブジェクトです。プレイヤーならPlayerController、AIならAIControllerが使われます。

弾を撃った側のPlayerControllerがEvent Instigator、弾のActorがDamage Causerになる関係
入力今回の攻撃弾の攻撃
Event InstigatorControllerプレイヤーキャラクターのGet Controller弾を撃った側のController
Damage CauserActorプレイヤーキャラクターのSelf弾のActor

Damage Causerは、物理的にぶつかった物だけを意味しません。近づくとダメージを与える焚き火なら、その焚き火のActorを発生源として渡せます。

受け側のAnyDamageでは、Controllerは Instigated By、発生源は Damage Causer のピンから取り出せます。倒したときのスコアを誰に加えるか、といった処理を足すための手掛かりになります。今回の実践では、この情報を渡すところまでにしています。

Damage Typeで攻撃の種類を伝える

通常攻撃なら20、炎なら防火装備で半減させたい。このように、同じ量でも種類で扱いを変えたいときに Damage Type を使えます。ダメージへ添える「炎」「毒」といった分類だと考えると分かりやすいでしょう。

たとえばDamageTypeを親にBlueprint Class BP_DamageType_Fire を作り、Apply Damageの Damage Type Class へ指定します。受け側はAnyDamageの Damage Type を使って種類を調べ、HPを引く前に倍率などを決めます。「炎のダメージを受けたときだけ半分にする」というルールを、受け側へ置けるわけです。

同じ20ダメージでも、Damage Typeを見て普通の敵は80、防火装備の敵は90になる

種類を渡すだけで炎の継続ダメージや点滅が始まるわけではありません。それらは別に作ります。また、Damage Typeは共通の設定を表すもので、攻撃のたびに残り時間を持つ入れ物ではありません。毒の残り秒数など、相手ごとに変わる値は受け側の変数で管理します。

体力の処理をComponentで使い回す

敵にも箱にも同じHP計算を作るようになったら、その処理を Actor Component、つまりActorへ付ける再利用可能な部品へまとめられます。

体力Componentの記事では、BP_HealthComponentApplyHealthDamage という関数を作っています。それを標準のダメージとつなぐには、ActorがAnyDamageで受け取り、その値をComponentへ渡します。

  1. 体力Componentの記事で作った BP_HealthComponent を、ダメージを受けるActorへ追加します。
  2. そのActorの Event AnyDamage から、追加したComponentの ApplyHealthDamage を呼びます。
  3. 呼び出しの Target はそのComponent、DamageAmount はAnyDamageの Damage です。
ActorのAnyDamageからHP ComponentのApply Health Damageを呼び、DamageをDamageAmountへ渡す接続

この方法へ切り替える場合、HP計算はComponent側へ任せます。Actor側にも今回の減算処理を残すと、別々のHPを管理することになります。今回追加した無敵時間も使いたいなら、その判定と解除TimerをComponentへまとめます。

HPバーを更新する段階では、Componentの記事の OnHealthChanged を使って、HPが変わったことをWidgetへ知らせられます。通知を受ける登録や初期表示も必要なので、Event Dispatcherの記事と合わせて進めてください。

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

  • 「1回の攻撃で1回だけ」と「被弾後しばらく無敵」は別のルール:今回の無敵時間は、別の敵からの攻撃もまとめて止めます。剣を1振りした間だけ同じ相手への重複を防ぐなら、攻撃側で「この振りで当てた相手」を記録する方法もあります。
  • 接触しているだけで毎フレーム減るわけではない:繰り返し減るかどうかは、攻撃側がいつApply Damageを呼ぶかで決まります。Begin Overlap は重なり始めの通知です。毎フレームのダメージを意図していないなら、Tickから何度も送っていないかを調べます。
  • 回復は別の入口にする:今回の実践は正のダメージ量を扱います。回復を加えるなら、無敵中の攻撃を止める判定と混ざらないよう、HPを増やす専用の関数を用意すると整理しやすくなります。
  • 死亡演出は消す前に始める:実践では結果を見やすくするため即座にDestroyします。死亡アニメーションを見せたい場合は、行動や当たり判定を止めてから演出を始め、終わった後に消す流れへ変えます。
  • オンライン対戦は別の段階で確認する:標準のダメージ適用とAnyDamageはサーバー側で扱う仕組みです。今回の1人用の実践から広げるときは、攻撃の要求、HPの同期、各画面での演出も設計します。
  • HPが減ったかは、受け側のCurrentHealthを見る:Apply Damageの戻り値を、そのまま自作HPの減少量と考えないでください。今回の無敵判定でHPを変えなかった結果は、受け側で確かめます。

まとめ

攻撃側は相手を調べ、Apply Damageで量を伝えます。受け側はEvent AnyDamageを入口にして、HPを減らし、0なら倒します。そこへ無敵中かどうかの判定を挟むと、被弾後しばらくは次のダメージを止められます。

まずは20ダメージで5回、40ダメージで3回という結果を確かめてみてください。量や無敵時間を変えても、攻撃側と受け側の役割は同じです。この形を土台に、剣、弾、爆発へと攻撃の種類を増やしていけます。

参考:Epicのダメージ処理の解説Apply DamageEvent AnyDamageApply Radial DamageDamage Type

やられたときに崩れ落ちる演出は Physics Assetとラグドール で作れます。

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