敵にも、壊せる木箱にも、砦のタワーにも体力を持たせたい。3つのBlueprintへ同じ体力処理をコピーすれば動きますが、ダメージの計算を変えるたびに、3か所を直すことになります。
そんなときに使えるのが、自作のBlueprint Component です。体力の計算を「差し込む部品」として1回作り、必要なActorへ追加します。敵と木箱を同じ専用の親クラスに揃えなくても、同じ機能を使えます。
今回は、体力100の敵役と、体力30の箱へ同じ部品を付けます。25ずつ減らす操作を用意し、残りHPと、力尽きたときの反応を見ながら、共通にする部分と各Actorへ任せる部分を整理しましょう。
この記事でわかること
- Actorへ同じ機能を追加して使い回す考え方
- ActorComponentとSceneComponentの違い
- 体力を減らす関数と、変化を知らせるDispatcherの作り方
- 敵と箱で同じ計算を使い、異なる反応を返す実践
Blueprint Component:Actorへ足す機能の部品
Static Mesh Componentが見た目を、Projectile Movement Componentが弾の移動を受け持つように、ComponentはActorへ機能を追加するための部品です。見た目だけでなく、体力や所持品を管理する部品もBlueprintで作れます(→ ActorとComponentの基本)。
他のエンジンでいうと: Unity の Component とほぼ同じ考え方です。Godot では、機能ごとに 子Node を足す形が近くなります。

例えばBP_HealthComponentを敵と箱へ1個ずつ追加すると、体力の計算方法は共通でも、現在のHPは別々 に持ちます。箱がダメージを受けても、敵のHPまで減るわけではありません。
Componentを取り付けた本体のActorを Owner と呼びます。敵へ付けた部品のOwnerは敵、箱へ付けた部品のOwnerは箱です。Component単体を、Actorのようにレベルへ置いて使うのではありません。
| 比べること | Actor | Component |
|---|---|---|
| 今回の役割 | 敵役や箱という本体 | 体力を管理する部品 |
| 使い始める場所 | レベルへ配置する | ActorのComponentsへ追加する |
| 共通にする範囲 | 本体全体の設計 | 体力など、必要な機能のまとまり |
位置を持つ部品か、持たない部品か
Componentを自作するときは、まず親クラスを選びます。ここでいう Transform は、位置・回転・大きさをまとめたものです。
| 親クラス | 部品自身のTransform | 向いている例 |
|---|---|---|
| ActorComponent | 持たない | 体力、所持品、状態の管理 |
| SceneComponent | 持つ | 銃口の位置、エフェクトを出す基準点 |
体力の計算には、部品自身の位置が要らないのでActorComponentを使います。敵がどこへ移動しても「HPから25を引く」という仕事は変わりません。
SceneComponentは、親から見た位置や向きを持てる部品です。ただし、SceneComponentそのものに見た目や当たり判定があるわけではありません。描画ならStatic Mesh、箱型の当たり判定ならBox Collisionなど、それぞれの機能を持つ派生Componentを使います。

実践:体力の部品を敵と箱へ付ける
まず、Fキーで敵役へ25ダメージ、Gキーで箱へ25ダメージを与えます。弾やHPバーを作る前に、部品へ頼んだ計算と通知が動くかをPrint Stringで確かめます。
到達点は、敵役が 100→75→50→25→0、箱が 30→5→0 と減り、それぞれのActorが力尽きたときの反応を決める状態です。

① 体力Componentを作る
Content Browserで「Blueprint Class」を作り、親クラスをActorComponent、名前を BP_HealthComponent にします。親が一覧に見えない場合は「All Classes」でActorComponent を検索します。
Variablesへ次の2つを作り、Compile後に既定値を設定します。
| 変数 | 型 | 既定値 | 設定・用途 |
|---|---|---|---|
MaxHealth | Float | 100.0 | Instance Editableをオン。取り付け先で最大HPを変える |
CurrentHealth | Float | 0.0 | 現在のHP。Component内で更新する |
Instance Editableは、同じ部品でも取り付けた個体ごとに値を変えられる設定です。今回はMaxHealthへ100または30を入れ、0より大きい値で使います。
ComponentのEvent Graphで Event BeginPlay → Set CurrentHealth をつなぎ、値へGet MaxHealthを入れます。開始時に、その個体の最大HPで満タンにします。
② 変化を知らせる出口を作る
My BlueprintのEvent Dispatchersの「+」から、次を作ります。
| Dispatcher名 | DetailsのInputsへ追加する値 | 知らせること |
|---|---|---|
OnHealthChanged | NewHealth:Float | HPを更新したことと、新しい値 |
OnDied | なし | HPが0になったこと |
Event Dispatcher は、出来事を、それを受け取るように設定した相手へ知らせる仕組みです。Componentは「HPが0になった」と知らせ、敵や箱が「その後どうするか」を決めます。
通知を送る操作が Call、受け取るイベントを 登録する操作が Bind です。名前を作るだけでは通知も反応も起きないので、送る側と受け取る側を順に組みます。
③ ダメージを受け付ける関数を作る
Functionsから ApplyHealthDamage を作り、Pureはオフ、InputsへFloatの DamageAmount を追加します。これは今回作る独自の関数です。UE標準の Apply Damage ノードとは別なので、名前を区別しておきます。
最初に「現在のHPが0より大きい」かつ「ダメージが0より大きい」を調べます。
- Floatの
>でCurrentHealthと0.0を比べる。 - もう1個の
>でDamageAmountと0.0を比べる。 - 2つの結果を
AND Booleanへ入れ、その結果をBranchのConditionへつなぐ。 - 関数の入口からBranchへ白い実行線をつなぎ、True側だけを次の処理へ進める。False側はそのまま終える。
ANDは両方の条件を満たすときだけtrueになります。これで、HPが尽きた後の呼び出しや、0・負数のダメージは処理しません。

図のGet DamageAmountは、この関数へ渡された入力を読むノードです。関数内の右クリック検索から置くか、入口のDamageAmount出力を使えます。同じ名前のClass変数を新しく作る必要はありません。
True側へSet CurrentHealthを置きます。値は次の順で計算します。
| ノード・入力 | 渡す値 |
|---|---|
Floatの減算 - のA | Get CurrentHealth |
| 減算のB | 関数入力のDamageAmount |
| Clamp (Float)のValue | 減算の結果 |
| ClampのMin / Max | 0.0 / Get MaxHealth |
| Set CurrentHealthの値 | Clampの結果 |
Clamp は、値を指定した範囲へ収めるノードです。箱のHPが5のときに25ダメージを受けても、-20ではなく0になります。減算とClampには白い実行線をつながず、値の線だけを使います。

Set CurrentHealthの白い出力から、次の順につなぎます。
Call OnHealthChanged。NewHealthへ、更新後のCurrentHealthを渡す。CurrentHealth <= 0.0をConditionにしたBranch。- True側から
Call OnDied。False側はそのまま終える。
各DispatcherをMy Blueprintからグラフへドラッグして「Call」を選べます。TargetはこのComponent自身のselfです。表示へ渡す値は、もう一度減算した結果ではなく、Setで更新した後のCurrentHealth を使います。

これで部品の仕事は「受け付けてよいか確認する→HPを更新する→変化を知らせる」となりました。敵の見た目や、箱が壊れる演出は、ここへ書きません。
④ 敵役と箱へ取り付ける
動作を試すレベルは、Third PersonのBlueprintプロジェクトで用意できます。Variantがある版はNoneを使います。Actorを親にした BP_Enemy と BP_Crate を別々に作り、次の見た目を追加します。
| Actor | Static Meshの例 | BP_HealthComponentのMaxHealth |
|---|---|---|
| BP_Enemy | エンジン標準のCylinder | 100.0 |
| BP_Crate | エンジン標準のCube | 30.0 |
今回は敵のAIや木箱の素材は不要です。形の違う2つの本体で、同じ部品を使えるかを確かめます。メッシュが見つからない場合は、アセット選択欄でShow Engine Contentを有効にし、EngineのBasicShapesから選びます。
それぞれのActorのComponentsで「Add」からBP_HealthComponentを1個追加します。追加した部品を選び、DetailsでMaxHealthを表の値に設定してCompile・保存します。2つのActorを、Play開始位置から見える床の上へ並べて置きます。
⑤ Actor側でHPの通知を受け取る
まずBP_EnemyのEvent Graphを開きます。Componentsから追加した体力部品をドラッグして参照を置き、そのピンから Bind Event to OnHealthChanged を作ります。
Event BeginPlayをBindの白い実行入力へつなぎます 。Bindの赤いEventピンからドラッグしてCustom Eventを作り、名前を HandleHealthChanged にします。Dispatcherに合わせたFloatのNewHealth入力が付きます。
このCustom Eventの後ろへPrint Stringを置きます。Append のAを Enemy HP: 、BをNewHealthのString変換にし、結果をPrint StringのIn Stringへ渡します。Durationは10秒にします。
箱にも同じ設定を行い、ラベルを Crate HP: に変えます。登録するBindはBeginPlayから、反応するPrintはCustom Eventから つなぎます。Bindの直後にPrintを置くと、HPが変わったときの反応にはなりません。

この図は登録の接続を見せるため、NewHealthのString変換とAppendを省いています。HP表示は上の手順どおり、NewHealthから作った文字列をIn Stringへつないでください。
⑥ 力尽きたときの反応を分ける
同じ体力部品の参照から Bind Event to OnDied を作り、先ほどのBindの白い出力から続けます。赤いEventピンからCustom Eventを作り、名前を HandleDied にします。
各ActorのHandleDiedから、次を実行します。
| Actor | 力尽きたときの処理 |
|---|---|
| BP_Enemy | Print String「敵が倒れた」→ Set Actor Hidden In Game(true)→ Set Actor Enable Collision(false) |
| BP_Crate | Print String「箱が壊れた」→ Destroy Actor |
どちらもTargetはselfです。敵役は見えなくして当たり判定を止め、箱はActor自体を消します。実際の敵なら死亡アニメーション、箱なら破片の演出へ置き換えられます。
HPを減らす計算は共通、HPが0になった後は各Actorが決める。ここが、この部品を使い回すための分担です。
⑦ キーから部品へダメージを渡す
攻撃側は「敵Classか、箱Classか」を調べる代わりに、相手に体力Componentがあるか を調べます。今回は、Level Blueprintから2つのActorを指定して試します(→ Level Blueprintと配置Actorの参照)。
Level Blueprintを開き、Functionsへ DamageTarget を作ります。Pureはオフ、InputsへActorのObject Reference型で TargetActor を追加します。Object Referenceは、今回処理する相手の個体を指す値です。
関数内を次の順でつなぎます。
- 入口から、白い実行ピンのある
Is Validへつなぐ。Input ObjectはTargetActor。 - TargetActorから
Get Component by Classを作り、Component ClassをBP_HealthComponentにする。 - 最初のIs Validの「Is Valid」側から、2個目のIs Validへつなぐ。そのInput ObjectはGet Component by ClassのReturn Value。
- 2個目の「Is Valid」側から、部品の
ApplyHealthDamageを呼ぶ。Targetは同じReturn Value、DamageAmountは25.0。
Is Valid は、参照先が存在し、まだ使えるかを確かめます。最初の確認は「箱がすでに壊れていないか」、次の確認は「体力の部品を持っているか」です。どちらもIs Not Valid側は何もしませ ん。
Get Component by Classは値を返すノードなので、白い実行線をつなぎません。Component Classを先にBP_HealthComponentへ指定すると、Return Valueから、その部品のApplyHealthDamageを呼び出せます。

Get TargetActorも関数入力を読むノードです。続く図は同じDamageTarget内の後半で、取得した部品を2個目のIs ValidとApplyHealthDamageの両方へ渡します。

CompileしてEvent Graphへ戻り、FとGのキーイベントを作ります。それぞれPressedからDamageTargetを呼びます。
| 入力 | DamageTargetのTargetActor |
|---|---|
| FのPressed | レベルに置いたBP_Enemyの参照 |
| GのPressed | レベルに置いたBP_Crateの参照 |
参照は、Outlinerで対象を選び、Level Blueprintの右クリックから「Create a reference to[名前]」で置けます。設計図ではなく、レベルへ置いた個体を選んでください。
⑧ HPの減り方と、共通の変更を確かめる
Component、 両Actor、Level BlueprintをCompile・保存してPlayします。ゲーム画面をクリックしてから、キーを離して押す操作を繰り返します。
| 操作 | 期待する結果 |
|---|---|
| Fを1回ずつ、合計4回押す | Enemy HPが75→50→25→0。4回目に「敵が倒れた」と出て姿が消える |
| Gを1回ずつ、合計2回押す | Crate HPが5→0。2回目に「箱が壊れた」と出て箱が消える |
| 消えた後、さらに同じキーを押す | 新しい死亡メッセージは出ず、参照エラーも出ない |
最後の確認では、敵はComponentのHPチェック、箱はActorのIs Validが、それぞれ続きを止めます。画面のメッセージは上下の位置ではなく、Enemy/Crateのラベルで見分けてください。
動かない場合は、F・Gの直後のPrintで入力を確認し、次にTargetActor、追加したComponent、Bindの実行線とEvent接続を確認します。HPの表示は変わるのに姿が消えないなら、OnDied側のBindとHandleDiedを調べます。
次にPlayを止め、BP_HealthComponentの減算へ入れるDamageAmountを、DamageAmount × 0.9 に変えてみます。25ダメージの10%軽減なので、実際に引く量は22.5 です。
再びPlayすると、Fの1回目は77.5、Gの1回目は7.5になります。敵役は5回、箱は2回でHPが0になります。両Actorの計算を書き換えずに、部品1か所の変更が反映されたことを確かめ、確認後は元のDamageAmountへ戻します。
ComponentとOwnerの役割を分ける
今回の体力Componentは、Ownerが敵か箱かを区別していません。HPの計算をして、登録された相手へ変化を知らせています。
| 方向 | 今回のやり取り |
|---|---|
| 外からComponentへ | ApplyHealthDamageで、減らす量を渡す |
| Componentから外へ | OnHealthChanged/OnDiedで知らせる |
| Owner自身の反応 | 表示する、見えなくする、消す |
もしComponentの中へ Get Owner → Cast To BP_Enemy → 敵の死亡処理 と書くと、その処理にはBP_Enemyが必要になります。箱へ取り付けても、同じCastは成功しません。
Castそのものが禁止なのではありません。特定のCharacter専用の移動部品など、使えるOwnerを決めた設計もあります。敵と箱の両方で使う今回の部品では、敵だけの処理を必須にしない ことが大切です。

通知は、ゲーム中の全Actorへ無条件に届く放送ではありません。そのComponentのDispatcherへBindした相手だけが受け取ります。部品ごとに通知先も別なので、敵のHP変更で箱のHandleDiedが 呼ばれるわけではありません。
おまけ:先に知っておくと良いこと
弾・HPバー・破片をつなぐには
弾が当たった相手へ、今回のDamageTargetと同じ確認を行えば、体力部品を持つ相手へダメージを渡せます。入力や弾の準備はSpawn ActorとProjectile Movementへ進めます。
標準のApply Damageを使う場合は、ActorのEvent AnyDamageで受けた量を、自作のApplyHealthDamageへ渡す方法があります。Componentを追加するだけで自動接続されるわけではありません(→ 体力とダメージ)。
HPバーはOnHealthChangedで受け取った値から更新し、破片や光はOwnerのHandleDiedへ加えます。UMGとNiagaraでそれぞれ作れます。
同じComponentを2個付けたら?
Get Component by Classは、指定したClassに一致する最初の部品を1個返します。今回のBP_HealthComponentは、各Actorに1個だけ追加します。部位ごとに体力を持たせたいなら、どの部品を攻撃するかも区別する必要があります。
毎フレームの処理は、必要になってから
今回の体力計算は、ダメージ関数が呼ばれたときだけ動きます。ComponentのClass DefaultsでStart with Tick Enabledを確認し、不要ならオフにします。Tickの既定値に頼らず、必要な処理があるかで判断します(→ Tickとイベントの使い分け)。
通知を受け取る相手が変わる場合
今回のOwnerは、自分に付いたComponentへBeginPlayでBindします。別のActorの部品を監視するUIなどでは、表示対象を替えたとき に古い接続をUnbindしてから、新しい相手へBindします。
「誰の通知を、いつまで受けたいか」を決めると、解除する場所も見えてきます。詳しい接続・解除はEvent Dispatcherで扱います。
自作の前に、標準の部品も探す
回転にはRotating Movement、弾の移動にはProjectile Movementなど、標準Componentもあります。コイン集めゲームでは、回転するコインにRotating Movementを使っています。
まとめ
BP_HealthComponentへ体力の計算をまとめ、敵役と箱へ1個ずつ追加しました。計算の定義は共通でも、HPと通知先は各個体が持ち、力尽きた後の反応はOwnerへ任せています。
似た処理を複数のActorへ書いていたら、何を共通の機能にし、何を本体の仕事に残すか考えてみてください。部品同士の連携を増やすときは、アクター間通信の3つの方法も判断の助けになります。