攻撃が当たった相手が敵ならHPを減らす。木箱なら壊す。対象が増えるたびに「敵か? 箱か? タワーか?」とCastを並べていると、攻撃側にも相手の種類を追加し続けることになります。
Blueprint Interface を使うと、相手へ「この量のダメージを受けてください」と同じ呼び方で頼めます。どう反応するかは、受け取った側が決めます。新しい対象を追加しても、攻撃側の呼び出しを増やさずに済む仕組みです。
この記事では、敵と箱のHPを実際に減らしてから、Castとの違いや参照関係の調べ方へ進みます。通信3方式の使い分けで紹介したInterfaceを、自分で作って使うための実践です。
この記事でわかること
- Interfaceに決めることと、受け手に作る処理の違い
- 同じMessageノードで、敵と箱のHPを減らす方法
- Targetの指定と、未対応の相手に送ったときの動き
- Castの使い分けと、Reference Viewerで確認できること
Interfaceは、共通の依頼書
敵と箱では、見た目も役割も違います。それでも「ダメージを受ける」という入口をそろえれば、攻撃側はどちらにも同じように頼めます。
Interfaceには、その入口の 名前と、受け渡す値 を決めます。今回は ReceiveWeaponHit という名前で、ダメージ量を渡すことにします。
| 担当 | 今回決めること |
|---|---|
| BPI_Damageable | ReceiveWeaponHitという機能に、DamageAmountという小数値を渡す |
| 敵・箱 | ReceiveWeaponHitを受けたら、自分のHPを減らす |
| 攻撃側 | 対象を指定し、ReceiveWeaponHitで25を渡す |
実装する とは、入口の先に実際の処理を書くことです。Interfaceは依頼書なので、HPを保管す る変数や、HPを減らす処理は持ちません。その中身は敵や箱のBlueprintへ作ります。

呼び出すときには、Targetという宛先が必要 です。同じMessageノードを使えても、1回の呼び出しで処理するのは指定した相手です。周囲の敵や箱へ一斉に届くわけではありません。
実践:敵と箱へ25ダメージを渡す
Fキーで、指定したActorへ25ダメージを渡す小さな実験を作ります。敵を指定するとHP100→75、箱を指定すると30→5。相手を変えても、送る側のグラフは同じ という点を確かめます。

UE5のThird PersonテンプレートをBlueprintで作り、VariantがあればNoneを選びます。新しい練習用フォルダへ作れば、既存記事の同名Blueprintと混ざりません。変数の作成とGet・Setは変数の基礎が前提です。
今回はHPの変化をPrint Stringで確認します。攻撃アニメーションや死亡演出は付けず、HPが0になって もActorは残します。
① 依頼書を作る
コンテンツブラウザで右クリック →「Blueprints」→「Blueprint Interface」を選び、BPI_Damageable を作ります。
- 開いたときの関数名を
ReceiveWeaponHitに変える。 - その関数のDetails → Inputsへ
DamageAmountを追加し、型をFloatにする。 - Outputsは空のままにし、Compile・保存する。
Float は25や12.5などの小数を扱う型です。DamageAmountは、呼び出すときに渡す 引数 で、「今回いくつHPを減らすか」を表します。
ここでHPを減らすノードを置けないのは正常です。依頼の形式ができたので、次に受け手の処理を作ります。
② 敵と箱のActorを用意する
「Blueprint Class」→「Actor」から、BP_Enemy と BP_Crate をそれぞれ作ります。どちらも親はActorで、別々のClassにします。
| 設定 | BP_Enemy | BP_Crate |
|---|---|---|
| 追加する部品 | Static Mesh | Static Mesh |
| メッシュ | エンジン標準のCylinder | エンジン標準のCube |
| 変数 | Health、Float | Health、Float |
| Healthの既定値 | 100 | 30 |
メッシュが見つからなければ、アセット選択のSettingsで「Show Engine Content」をオンにし、Engine/BasicShapes のものを使います。部品の位置・回転は0、Scaleは1、Simulate Physicsはオフにします。
変数を追加したらCompileし、Healthの既定値を入力します。 保存して両方をレベルへ置き、ActorのScaleは1、中心が床から50cm程度の位置にします。操作キャラクターから見やすい場所へ並べれば十分です。
次に両方のBlueprintを開き、「Class Settings」→「Interfaces」の追加欄から BPI_Damageable を選び、Compileします。
これで「このClassはReceiveWeaponHitに対応する」と宣言できました。まだHPを減らす処理はないので、続けて実装します。
③ 受け取った量だけ、自分のHPを減らす
BP_EnemyのEvent Graphで右クリックし、Interfaceの Event ReceiveWeaponHit を追加します。同じ名前のCustom Eventを新しく作るのではなく、追加したInterfaceのイベントを選んでください。
Event ReceiveWeaponHitには、白い実行出力とDamageAmountの値の出力があります。次のようにつなぎます。
- 白い実行出力をSet Healthへつなぐ。
- Floatの減算を作り、AにGet Health、BにイベントのDamageAmountをつなぐ。
- 減算の結果をFloatのMaxへ入れ、もう一方を0にする。
- Maxの結果をSet Healthの値へつなぐ。

Max は、2つの値の大きい方を返すノードです。HPが5のときに25を引くと−20ですが、0と比べて大きい方を選ぶので、HPは0で止まります。この練習では正のダメージ量だけを送ります。
Set Healthの後ろへPrint Stringをつなぎ、Durationを10秒にします。AppendのAを Enemy HP: 、Bを 更新後のGet HealthをStringへ変換した値 にし、結果をPrintのIn Stringへ渡します。

図の「Set Healthの実行出力から」は、前の図とのつなぎ目を示す注記です。To String(Float)は数値を文字列にし、Appendは前後の文字列を連結します。
BP_Crateにも同じ処理を作り、AppendのAだけ Crate HP: にします。Compile・保存してください。敵と箱はそれぞれ自分のHealthを持つので、箱へのダメージで敵のHPが減ることはありません。
④ 相手を指定して、Messageを送る
Actorを親に BP_DamageSender を作ります。送信を担当するだけなので、見た目の部品は不要です。
変数 TargetActor を作り、型を ActorのObject Reference、Instance Editableをオンにします。Object Referenceは、レベルにいる相手の個体を指定する値です。ここではBP_Enemy専用の型にせず、箱も受け取れるActor型にします。
Event GraphへCustom Event SendHit を作り、次をつなぎます。
- SendHitから、白い実行ピンのあるIs Validへつなぐ。
- Is ValidのInput ObjectにGet TargetActorを入れる。
- Get TargetActorの青いピンからドラッグし、
ReceiveWeaponHit(Message)を検索して置く。 - Is Validの成功側をMessageの白い入力へつなぎ、Damage Amountを25にする。TargetはGet TargetActor。
封筒アイコン付きのMessageノード を選びます。BP_DamageSender自身にInterfaceを追加する必要はありません。受け取る敵と箱が実装していれば呼べます。
Is Validは、相手が存在して使えるかの確認です。Is Not Valid側にはPrint Stringで Target is not set を10秒表示し、宛先の指定忘れが分かるようにします。

Compile・保存し、BP_DamageSenderをレベルへ1個置きます。そのActorのDetailsにあるTarget Actorへ、配置したBP_Enemy を指定します。変数を作っただけでは相手は入りません。
最後に、配置したBP_DamageSenderを選んだままLevel Blueprintを開きます。右クリック →「Create a reference to[名前]」で参照を作り、その青い出力からSendHitを呼びます。FキーのPressedをSendHitの白い入力へつなぎます。

Fキーはこの練習の入力なので、Level Blueprintで受けます。BP_DamageSenderのAuto Receive Inputを変更する必要はありません。
⑤ 相手を変えても、同じ呼び出しが使えるか確かめる
Compile・保存してPlayし、ゲーム画面をクリックしてからFを1回押します。画面に Enemy HP: 75 と出れば、送った25が敵へ届いています。
Playを止め、レベルのBP_DamageSenderのTarget Actorを、配置したBP_Crateへ変えます。グラフは編集せず、再びPlayしてFを押してください。
| 操作 | 期待する表示 |
|---|---|
| 敵を指定して、新しくPlay→F | Enemy HP: 75 |
| 箱を指定して、新しくPlay→F | Crate HP: 5 |
| 同じPlayで、もう一度F | Crate HP: 0 |
| さらにF | Crate HP: 0。負の値にはならない |
相手を差し替えるだけで、同じReceiveWeaponHitから別々のClassの処理を呼べました。送る側は敵のHealthも箱のHealthも直接書き換えず、ダメージ量だけを渡しています。
次は、レベルへ普通のCubeを置き、Target ActorをそのCubeにして試します。このCubeにはBPI_Damageableを追加していないので、Fを押してもHPの表示は出ません。Messageは、Interface未対応の相手では処理を呼びません。
Cube自体は存在するため、Is Validは成功します。「存在する」と「その依頼に対応している」は別の確認だと分かります。確認後はTarget Actorを敵へ戻してください。
ダメージ量も変えたいなら、Messageの25を40へ変えて、新しいPlayで敵に送ります。100→60になれば、受信側が固定の25ではなく引数を使えています。確認後は25へ戻します。
Castとハードリファレンスを分けて考える
Cast は、参照している相手を、指定したClassとして扱えるか調べるものです。成功すれば、そのClass固有の変数や関数を使えます。別のActorを生成したり、箱を敵へ作り変えたりする処理ではありません。
他のエンジンでいうと: C# の
as/is、GDScript のasに近い「型を確かめて、その型として扱う」操作です。GetComponent<T>()のような 部品の取得とは別物 です。
たとえば敵専用の攻撃パターンを変更したいなら、BP_Enemyとして扱う必要があります。一方、今回のように「ダメージを受ける」という共通機能だけが必要なら、相手ごとにCastを並べずInterfaceで頼めます。
ここに、もう一つ別の話として アセットの依存関係 があります。依存とは「自分が動くために、別のアセットも必要」というつながりです。
Cast To BP_Enemy を置いたBlueprintは、BP_EnemyというBlueprintアセットへの ハードリファレンス を持ちます。これは、参照元を読み込むときに参照先も必要になるつながりです。BP_Enemyがさらにメッシュや音をハード参照していれば、その先へ読み込みが連なります。
そのため、今は必要ない敵や装備のBlueprintまで広く参照していると、読み込みやメモリ使用の負担につながることがあります。これは Castを1回実行する時間 と 、参照先のアセットを読み込む負担 を分けて考える話です。「Castがあるから遅い」とは判断できません。
Interfaceにすると、送信側は具体的なBP_Enemyの代わりに、共通のBPI_Damageableを使えます。ただし依存がゼロになるわけではありません。Interface自身への参照や、ほかの変数・ノードに残った参照もあるため、次の方法で確かめます。

ハード参照と、必要になったときに読み込めるソフト参照の違いは、アセット管理の基礎でも紹介しています。
Reference Viewerで、依存する相手を確かめる
Reference Viewer は、アセット同士の「どれが、どれを使うか」を見る道具です。グラフが短くなったかだけでなく、送信側が敵の種類を直接知る必要がなくなったかを調べられます。
- BP_DamageSenderをCompile・保存する。
- コンテンツブラウザでそのアセットを右クリックし、「Reference Viewer」を開く。
- 中央のBP_DamageSenderから、右側の依存先を確認する。
- BPI_Damageableへの参照を探し、BP_EnemyやBP_Crateへ直接依存する経路がないかを見る。
調べるのは BP_DamageSenderアセット です。レベル全体を開くと、そこへ置いた敵や箱の参照も出るので、別の調査になります。今回のTarget Actorの割り当ては、レベルに置いたSenderの個体に対して行っています。
自分の既存グラフをCast版から変更する場合は、変更前の画面を残して比べます。「View」の参照種別や検索の深さ・表示数をそろえ、同じ条件で更新してください。フィルタで隠れた線を、消えた依存と取り違えないようにします。
BP_Enemyへの参照が残っていたら、BP_Enemy型の変数、ほかのCast、Classを指定するノードなどを探します。Messageへ1か所置き換えるだけで、別経路の参照まで消えるわけではありません。

サイズの内訳を見たいときは、同じアセットを右クリック →「Size Map」を開きます。アセットと依存先のサイズを図で確認できます。比較するならDisk Size/Memory Sizeなどの表示条件をそろえ、大きな割合を占める参照先を調べます。
Reference Viewerの線は依存関係、Size Mapはサイズを見るものです。線が3本減ったから何秒速くなった、とは分かりません。 実際の読み込み時間は、別の参照経路や、すでに読み込まれているアセットなどにも左右されます。速度の改善を確かめたい段階では、Unreal Insightsなどで同じ条件の実行を計測します。
呼んでも動かないときに見るところ
| 症状 | 確認するところ |
|---|---|
| Target is not setと出る | レベルのSenderに、配置済みActorを割り当てたか |
| 相手はいるのにHPが出ない | 相手のClass SettingsへBPI_Damageableを追加したか |
| Interfaceは追加済み | Event ReceiveWeaponHitから、白い線がSet HealthやPrintへ続いているか |
| 40を送っても25しか減らない | 減算のBに、イベントのDamageAmountを使っているか |
| Messageが見つからない | BPIをCompile・保存し、Actorの青い参照ピンから検索したか |
| 別の相手が反応する | MessageのTargetが、今回処理したい個体を指しているか |
未対応の相手では何も起きないため、送信側から見るだけでは実装忘れに気付きにくいことがあります。迷ったら、受信側のEvent ReceiveWeaponHit直後へ一時的にPrintを置き、まず届いているかを調べると切り分けやすくなります。
Does Implement Interface は、相手がそのInterfaceに対応するかを返す判定です。Test Objectへ相手の参照、InterfaceへBPI_Damageableを指定すると、結果がBooleanで返ります。BooleanはTrue/Falseの2通りの値です。

これはMessageの前に必ず挟むものではあ りません。「対応している相手だけ照準を変える」「未対応なら別の案内を出す」のように、対応の有無で処理を選びたいとき に使います。単に対応する相手へ依頼を送るだけなら、今回のMessageで足ります。
おまけ:先に知っておくと良いこと
攻撃の当たり判定とつなぐ
今回のTargetActorは手で割り当てましたが、実際の攻撃では、Line TraceのHit Actorや、弾が重なったOther ActorをMessageのTargetへ渡します。「相手を見つける処理」と「見つけた相手へ頼む処理」を分けておくと、剣でも弾でも同じ入口を使えます。
標準の Apply Damage と Event AnyDamage でもダメージを扱えます。ここで作ったReceiveWeaponHitはInterfaceを学ぶための独自機能で、標準のダメージイベントを自動的に呼ぶものではありません。標準機能を使う実践は体力とダメージの記事へ進めます。
体力Componentと組み合わせる
体力Componentの記事のBP_HealthComponentを使うなら、ActorのEvent ReceiveWeaponHitから、部品の ApplyHealthDamage を呼びます。Targetに自分のHealthComponent参照、Damage Amountに受け取ったDamageAmountを渡します。
その場合、この記事のActor側のHealth変数と減算は部品の処理へ置き換えます。Interfaceが「 ダメージの入口」、Componentが「体力の保管と計算」という分担です。部品を追加するだけで、Actor宛てのMessageが自動転送されるわけではありません。
Interfaceにも戻り値を定義できる
今回は依頼を送るだけなのでOutputsを空にしました。たとえば GetInteractionText にText型の戻り値を付ければ、対象ごとに「開ける」「調べる」などの表示文字を返せます。戻り値を持つ場合は、受信側では関数として中身を実装します。
ただし、Messageの相手が未対応なら処理は実行されません。戻った値だけで対応済みと判断せず、必要な場面ではDoes Implement Interfaceも使います。
依頼書には、受け渡しに必要な型を選ぶ
Interfaceの入力をBP_Enemy専用の参照型にすると、依頼書自体がそのBlueprintを知る必要があります。今回ならFloatのダメージ量だけで十分です。種類を問わない相手の参照が必要ならActor型を検討するなど、具体的なClassを使う理由があるか考えます。
C++でも、入口と実装を分けられる
C++ではUInterfaceを使って共通の入口を定義できます。最初からC++へ移す必要はなく、Blueprintで役割が分かってからBlueprintからC++への第一歩へ進めば十分です。
まとめ:CastとInterfaceの使い分け
| やりたいこと | 選び方 |
|---|---|
| そのClass固有の変数や関数を使いたい | Castで指定Classとして扱えるか確認する |
| 種類の違う相手へ、共通の機能を頼みたい | Interfaceで入口と受け渡す値をそろえる |
| 出来事を複数の受け手へ知らせたい | Event Dispatcherへの登録と通知を使う |
敵と箱で試したように、Interfaceを使うと送信側は「誰の内部をどう変えるか」まで決めず、共通の依頼を渡せます。受け手を追加するときは、Interfaceの追加と、そのActorらしい反応を用意します。
参照関係を調べるときも、Castの数だけを減らすことを目標にせず、必要な相手へつながっているかを確認してください。出来事を複数の担当へ知らせる仕組みは、Event Dispatcherの記事で試せます。