敵が増えたら動きが重くなった。倒した敵を調べるとエラーが出る。久しぶりに開いたグラフで、どこを直せばよいか分からない。Blueprintが動くようになった後は、こうした困りごとが出てきます。
この記事では、よくある10のつまずきを 「重い」「動きがおかしい」「読めない」 の3つから調べます。使ってはいけないノードの一覧ではなく、「今の使い方が、この場面に合っているか」を見直すためのチェックリストです。
最後は、目印の位置を表示する小さな例で、相手の探し方と、相手が消えた後の処理を直します。既存のBlueprintを見直すときも、同じように1箇所ずつ確かめていきましょう。
この記事でわかること
- 処理の回数、相手の探し方、計算の使い回しを見直す考え方
- 参照・Cast・物理・Dispatcherで、誤解しやすい使い分け
- 関数・変数・コメントで、直す場所を見つけやすくする方法
- 目印がある場合と、消えた場合の両方を確かめる実践
まず、どの症状が出ているかを見る
たとえば、画面がカクつく原因はBlueprintだけとは限りません。描画や物理の負荷もあります。一方、「敵を倒した直後だけ位置を読めない」なら、処理を速くする前に、読もうとしている相手を確かめる必要があります。
| 困っていること | 最初に見るところ |
|---|---|
| 数が増えると重い、読み込みが長い | 何を何回行うか、どのアセットに依存するか |
| 消えた相手でエラー、動きや通知がおかしい | 相手が有効か、機能を意図どおりに使っているか |
| 変更する場所が分からない | 処理のまとまり、変数の置き場所、名前 |
1体で動くことと、100体でも無理なく動くことは別の確認です。 ただし、100体に増やせば必ず問題が出るわけでもありません。使っている数・呼ぶ頻度・必要な動作を考え、症状に関係する項目から読みます。

グループ1:重くなる・読み込みが増える
ミス1:必要な頻度を考えず、Event Tickにつなぐ
Event Tick は、通常は毎フレーム呼ばれます。毎フレーム更新するActorが100体あり、60FPSで動いていれば、入口だけで毎秒6000回です。中で何をするかによって、その費用が変わります。
滑らかな追従は毎フレームでも、残り時間の表示は1秒ごとで十分かもしれません。範囲に重なり始めた瞬間だけ反応するならOverlapイベント、HPが変わったときだけ知らせるならEvent Dispatcherを使えます。
定期処理にはTimerも使えますが、軽くなる理由は、必要な回数へ減らせることです。同じ重い処理を同じ頻度で呼べば、Timerへ置き換えただけでは解決しません。TickとTimerの記事では、近づいた間だけ動く見張り塔で使い分けます。
ミス2:決まった相手を、毎回集め直す
Get All Actors Of Class は、指定したClass(Actorの種類)のActorを集め、配列=複数の値を並べた一覧で返します。該当するActorの数や呼ぶ頻度、その後の処理が増えると負荷も増えます。
決まった扉を1枚開けたいだけなら、その扉への 参照=後からその個体を指定するためのもの を持っておけば、毎回一覧を作らずに済みます。

図の60回/秒は60FPSで毎フレーム呼ぶ例です。最初の1回で済むのは、使う相手が変わらない場合です。
複数の敵を扱うなら、BeginPlayで取得した一覧を使い回す方法もあります。ただし、後から敵が生まれたり消えたりするなら、一覧も更新します。 生成したときに登録し、退場時に外す、と管理する方法もあります。
配列の0番目を取れば欲しい相手になる、とも限りません。見つからなければ配列は空です。最後の実践では、この「集めて先頭を取る」を、配置した1体の直接指定へ直します。
ミス3:Pureノードの結果が、保存されていると思う
変数のGetや足し算には、白い実行ピンがありません。このような Pure(純粋)ノード は、結果を使う処理から必要になったときに評価されます。グラフに1個置いたからといって、最初の計算結果をずっと覚えているわけではありません。
たとえば、重い計算の結果を別々のSetやPrintで使うと、その都度計算される場合があります。ループから使うときも、繰り返しの回数に注意します。線の本数だけで実行回数を決めつけず、どの処理が、いつその値を必要とするかを見ます。
同じ結果を使い回したいなら、一度変数へSetしてからGetします。ただ し「イベントのたびに計算し直す」「対象が変わったときに更新する」など、保存するタイミングも決めます。古い値を使い続けては、速くても動作が合いません。
軽いGetや足し算まで、すべて保存用の変数へ置き換える必要はありません。Pureは、値を求める処理に向いた形です。自作するときも、HPを書き換えるような処理を混ぜず、役割をそろえます(FunctionとMacroの記事)。
ミス4:相手の型を知る必要がない場所までCastする
Cast To BP_Enemy は、持っている参照の相手がBP_Enemyとして扱えるかを確かめる処理です。敵専用の値を読むなら役立ちます。相手を新しく探したり、別の種類のActorへ変えたりするノードではありません。
もう一つ見るのが、ハードリファレンス=アセット同士が、読み込みに必要な相手を直接指定する関係です。特定のBlueprintへCastすると、そのClassへの依存ができます。そこからメッシュなどへの依存がつながっていれば、読み込むものが増える原因になります。
同じ「ダメージを受けて」という用件だけを伝えたいなら、Blueprint Interfaceで共通の呼び口を用意できます。ただし、変数の型やほかのノードに具体的なClassへの依存が残っていれば、それも確認が必要です。Interfaceへ変えたら全依存が消える、という仕組みではありません。
Castの数だけで性能を判断せず、相手を知る必要があるかを考えます。 Interfaceの記事では、敵と箱へ同じ呼び方でダメージを渡し、Reference Viewerで依存も調べます。通信3方式の比較も判断に使えます。
グループ2:動きがおかしくなる
ミス5:相手が消えても、参照をそのまま使う
敵を倒した後も、UIがその敵のHPを読もうとする。ロックオンした敵が消えたのに、位置を調べ続ける。こうした場面では、参照先が使えなくなっていることがあります。未設定の参照も、相手を指していません。
Is Valid は、参照先が使える状態かを確かめます。相手を取得する処理が失敗し得る場所や、破棄され得るActorを使う直前で確認します。無効なら「表示を消す」「対象なしと知らせる」など、その場に合う処理へ分けます。
Blueprintでは、無効な参照を使うとAccessed Noneなどの実行時エラーにつながります。必ずゲーム全体が落ちる、という意味ではありませんが、期待した処理はできません。
そして、Is Validは、失った相手を探し直す機能ではありません。 プレイヤーがリスポーンしたなら、新しい個体への参照を取り直す仕組みが必要です。
ミス6:力を加えることと、速度を設定することを混同する
物理で動く箱に「押す力」を加えることと、「今の速度」を直接決めることは違います。ここを混ぜると、吹き飛ばしたいのに止まる、といった動きになります。
| したいこと | 主に使うもの | 考え方 |
|---|---|---|
| 推進力で押し続ける | Add Force | 力を加える間、毎フレーム呼ぶ使い方がある |
| 爆発などで一瞬押す | Add Impulse | その瞬間の押す効果を与える |
| 速度を直接決める | Set Physics Linear Velocity | 現在の運動をどう扱うか考えて設定する |
Add Forceを毎フレーム呼ぶこと自体は、ミスではありません。一方、Add to CurrentをオフにしたSet Physics Linear Velocityで同じ速度を毎フレーム設定すると、衝突などで変化した速度を上書きします。

普通のCharacterの歩行・ジャンプは、まず Character Movement Component の設定から考えます。物理を使う箱や乗り物とは、動かす仕組みを分けて理解しましょう。
ミス7:通知が必要な期間を決めずにBindする
Event Dispatcherの Bind は、通知が届いたら呼ぶ処理を登録することです。たとえば、開いている間だけ更新したい画面が、閉じた後も通知を受けると、不要な処理が残ります。
受信を終えるときは Unbind で登録を外します。BeginPlayとEndPlayを対にする場合もあれば、画面を開くときと閉じるときに分ける場合もあります。大事なのは、受信者が生きている期間と、通知が必要な期間は必ずしも同じではないことです。
BlueprintのDispatcherでは、無効になった受信者は呼び出し対象から外れます。一方、画面を閉じてもWidget自体を保持している場合は、受信が続くことがあります。今はもう必要ない通知を止める判断は、作る側が行います。
Unbind Eventでは、登録した相手とイベントをそろえます。Unbind All Eventsは他の登録も外すため、単に自分の通知を止めたいときは範囲に注意します(BindとUnbindの実践)。
グループ3:読めなくなる

ミス8:一つのグラフへ、違う役割を詰め込む
HPを減らす計算の途中に、画面表示や扉を開く処理まで並んでいると、変更したい場所を見つけにくくなります。まずComment Boxでまとまりを示し、「HPを更新する」「表示を更新する」と役割を分けます。
繰り返し呼ぶ処理や、名前を付けて読みたい処理はFunctionへ切り出せます。Collapse Graphは、その場所のノードを一つのまとまりに畳む方法です。内部も確認できますが、Functionのように別の場所から共通の処理を呼ぶ仕組みとは違います。
FunctionにはDelayのような、完了まで待つ処理は置けません。その場合はイベント側に残すか、置き場所の制約を確認してMacroなどを使います。分け方はグラフ整理の10テクニックで試せます。
ミス9:一時的な値まで、Class変数に残す
HPのように後の処理でも使う値は、その個体が持つClass変数に置きます。一方、「この計算中だけ使う合計」まで同じ場所へ置くと、前回の値が残り、2回目だけ合計がおかしくなることがあります。
関数の中だけで使う値は Local変数 にすると、その呼び出しの中で役割が完結します。たとえば合計用のLocal変数を既定値0にしておけば、関数を呼ぶたびに0から集計できます。
ただし、同じ呼び出しの中で集計をやり直すなら、そこでのリセットは別に考えます。「Localにすれば、どんなリセットも不要」とは覚えないようにします(Class変数とLocal変数の実践)。
ミス10:名前とコメントから、目的が分からない
Value1 と Value2 では、何を比べるのか伝わりません。現在のHPならHealth、追跡を始める距離ならChaseRangeなど、使う場面が浮かぶ名前にします。
コメントも「HPを0にする」だけでなく、「倒された後の追加ダメージを受け付けない」のように、判断の理由を残すと役立ちます。まとまりの見出しは「体力を更新」、迷いやすい分岐の近くには理由、と書き分けられます。
Booleanにbを付ける、InterfaceにBPI_を付ける、といった命名はチーム内でそろえる約束です。BlueprintのBooleanがbで始まらないと動かない、という制約ではありません。
実践:目印が消えても困らない処理へ直す
Fキーで目印の位置を表示し、Gキーでその目印を消す例を作ります。最初は「毎回探して、先頭の1体を使う」形です。それを 使う相手を直接指定し、いなくなったら位置を読まない 形へ直します。

図の座標は一例です。自分が置いた目印の位置と、画面に表示される数値を比べます。
今回は探索対象を減らす考え方と、無効な参照の分岐を練習します。キー入力で数回呼ぶだけなので、FPSの改善を体感する実験ではありません。
UE5のThird PersonテンプレートをBlueprintで作り、VariantがあればNoneを選びます。新しい練習用Levelを保存するか、テンプレートのLevelで作業します。
① 位置を調べる目印を1体置く
Actorを親に BP_PracticeMarker を作ります。DefaultSceneRootの子にStatic Meshを追加し、標準Cubeを設定します。相対位置・回転は0、Scaleは1、Collision PresetsはNoCollision、Simulate Physicsはオフにしま す。
Cubeが見つからない場合は、アセット選択欄の設定で「Show Engine Content」をオンにし、Engine/BasicShapesのCubeを選びます。
Compile・保存して、Levelへ1体だけ置きます。ActorのScaleは1にし、Player Startから見える場所へ移動します。中心は床から50cmほど上です。Outlinerで、この個体の名前を PracticeMarker に変えます。
目印を選び、DetailsのLocationにあるX・Y・Zをメモします。これは ワールド座標=Level全体の中での位置 です。後で表示される数値と比べます。
② 受け取ったActorの位置を表示する関数を作る
LevelのBlueprintメニューから「Open Level Blueprint」を開きます。ここにFunction PrintMarkerPosition を作り、白線で呼べるようにPureをオフにします。Inputsへ Marker を ActorのObject Reference 型で追加し、Outputsは作りません。Object Referenceは、実際のActorの個体を受け取る型です。
- 関数の入口の白線をPrint Stringへつなぎ、Durationを10秒にする。
- 入口のMarker出力からGet Actor Locationを作り、Targetへつなぐ。
- Get Actor LocationのReturn ValueをTo String(Vector)へ渡す。
- To StringのReturn ValueをPrint StringのIn Stringへつなぐ。
Vector は、ここではX・Y・Zの3つをまとめた位置の値です。To String(Vector)は、それを画面に表示できる文字へ変えます。この関数は、受け取った相手の位置を表示する係です。
