【Unreal Engine】Blueprintでよくある10のミスとその解決策

作成: 2025-12-12最終更新: 2026-09-05

Blueprintのつまずきを「重い・動きがおかしい・読めない」に分け、Tick・Cast・Pure・参照・物理・Dispatcherの直し方を解説します。

敵が増えたら動きが重くなった。倒した敵を調べるとエラーが出る。久しぶりに開いたグラフで、どこを直せばよいか分からない。Blueprintが動くようになった後は、こうした困りごとが出てきます。

この記事では、よくある10のつまずきを 「重い」「動きがおかしい」「読めない」 の3つから調べます。使ってはいけないノードの一覧ではなく、「今の使い方が、この場面に合っているか」を見直すためのチェックリストです。

最後は、目印の位置を表示する小さな例で、相手の探し方と、相手が消えた後の処理を直します。既存のBlueprintを見直すときも、同じように1箇所ずつ確かめていきましょう。

Blueprintのつまずきを、重い・壊れる・読めないという症状から調べる

この記事でわかること

  • 処理の回数、相手の探し方、計算の使い回しを見直す考え方
  • 参照・Cast・物理・Dispatcherで、誤解しやすい使い分け
  • 関数・変数・コメントで、直す場所を見つけやすくする方法
  • 目印がある場合と、消えた場合の両方を確かめる実践

Sponsored

まず、どの症状が出ているかを見る

たとえば、画面がカクつく原因は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方式の比較も判断に使えます。

Sponsored

グループ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で同じ速度を毎フレーム設定すると、衝突などで変化した速度を上書きします。

Add Forceは押し続ける力、Add Impulseは一瞬の押し、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:読めなくなる

1枚へ詰め込んだ処理を、移動・攻撃・被弾という役割に分ける

ミス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で始まらないと動かない、という制約ではありません。

Sponsored

実践:目印が消えても困らない処理へ直す

Fキーで目印の位置を表示し、Gキーでその目印を消す例を作ります。最初は「毎回探して、先頭の1体を使う」形です。それを 使う相手を直接指定し、いなくなったら位置を読まない 形へ直します。

目印がある間はFで位置を表示し、Gで消した後はFでMarker missingと知らせる

図の座標は一例です。自分が置いた目印の位置と、画面に表示される数値を比べます。

今回は探索対象を減らす考え方と、無効な参照の分岐を練習します。キー入力で数回呼ぶだけなので、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の個体を受け取る型です。

  1. 関数の入口の白線をPrint Stringへつなぎ、Durationを10秒にする。
  2. 入口のMarker出力からGet Actor Locationを作り、Targetへつなぐ。
  3. Get Actor LocationのReturn ValueをTo String(Vector)へ渡す。
  4. To StringのReturn ValueをPrint StringのIn Stringへつなぐ。

Vector は、ここではX・Y・Zの3つをまとめた位置の値です。To String(Vector)は、それを画面に表示できる文字へ変えます。この関数は、受け取った相手の位置を表示する係です。

関数のMarker入力からGet Actor Locationで位置を取得し、Vectorを文字に�してPrint Stringへ渡す接続

③ 毎回探して先頭を使う、修正前の形を作る

Level BlueprintのEvent Graphに、次の接続を作ります。

  1. キーボードのFイベントを置き、PressedをGet All Actors Of Classへつなぐ。Actor ClassはBP_PracticeMarker。
  2. Out ActorsからArrayのGet(a copy)を作り、Indexを0にする。
  3. Get All Actors Of Classの白い出力から、自作のPrintMarkerPositionを呼ぶ。
  4. Array Getの出力を、PrintMarkerPositionのMarker入力へ渡す。

Indexは、配列の何番目を取るかを指定する番号です。0から数えるので、Index 0は先頭を表します。自作関数の呼び出しは、このLevel Blueprint自身のものを使い、受け渡す目印をMarker入力へつなぎます。

Fを押すたびGet All Actors Of Classで目印を集め、Array GetのIndex 0をPrintMarkerPositionへ渡す修正前の接続

Compile・保存してPlayします。ゲーム画面をクリックし、Fを押して、X・Y・Zが先ほどのLocationと一致するか確かめます。小数点以下の表示桁が違っても、同じ位置の値なら成功です。

今は目印が1体なので動きます。ただ、毎回一覧を作り、その先頭が目的の相手だと仮定しています。目印が2体なら、どちらを選びたいかはこの接続から伝わりません。

④ 配置した目印を直接指定する

Playを停止します。LevelでPracticeMarkerを選び、Level Blueprintで右クリックして「Create a Reference to PracticeMarker」を作ります。これは、先ほど置いた その1体 を指す参照です。

Fの後ろにあったGet All Actors Of ClassとArray Getを外し、F PressedをPrintMarkerPositionへ直接つなぎます。Marker入力には、配置したPracticeMarkerへの参照を渡します。

Compile・保存し、新しくPlayしてFを押して、同じX・Y・Zが出るか確かめます。位置の表示はそのまま、使う相手の決め方だけが変わりました。Playを停止します。

⑤ 相手がいない場合の分岐を加える

F PressedとPrintMarkerPositionの間に、白い実行ピンを持つ Is Valid を置きます。Booleanを返すだけの形ではなく、Is Valid/Is Not Validへ実行を分ける形を選びます。

  1. Input Objectへ、同じPracticeMarkerの参照を渡す。
  2. Is Validの白い出力をPrintMarkerPositionへつなぐ。Marker入力も同じ参照。
  3. Is Not Valid側には別のPrint Stringを置き、In Stringを Marker missing、Durationを10秒にする。

Marker missingは「目印がいない」という確認用の表示です。無効な場合は、位置を取得する関数自体を呼びません。PrintMarkerPositionの中は変更しなくて構いません。

配置したPracticeMarkerをIs Validで確認し、有効なら位置を表示、無効ならMarker missingを表示する接続

⑥ Gキーで消し、両方の結果を確かめる

Level BlueprintにGイベントを追加します。G Pressedから別のIs Validへつなぎ、Input Objectには同じPracticeMarker参照を渡します。Is Valid側からDestroy Actorを呼び、Targetも同じ参照にします。Is Not Valid側は何もしません。

G Pressedから配置目印の有効性を確かめ、同じPracticeMarkerをTargetにしてDestroy Actorを呼ぶ接続

Destroy Actorは、そのPlay中の目印を破棄します。エディタに保存した配置は残るので、Playを開始し直せば目印も戻ります。

Compile・保存して、次の順に試します。

操作確認する結果
新しくPlayし、Fを押す目印のX・Y・Zが出る
Gを押すCubeの目印が消える
もう一度Fを押す位置の代わりにMarker missingが出る
GやFを繰り返す消えた相手の位置を読まず、参照の実行時エラーを増やさない
Playを停止し、開始し直してFを押す戻った目印の位置を表示できる

消えた後も位置を表示しようとする場合は、Fから関数へ直接行く白線が残っていないか、Is Validが同じ参照を調べているかを確認します。実行時エラーはOutput Logや、Play停止後のMessage Logでも調べられます。

最後に、Playを停止して目印のX座標を100cm動かし、再度PlayしてFを押します。新しい位置が表示されれば、参照を使い回すことと、位置の値を固定することは違う と確かめられます。確認後は元の位置へ戻します。

この練習では、使う1体が最初から決まっていました。ゲーム中に別の目印へ切り替えるなら、選択した相手をActor参照の変数へ保存し直す、と発展させられます。Is Validだけで新しい相手へ切り替わるわけではありません。

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

重さは、直す前と後を同じ条件で比べる

最適化では、見た目の動作確認に加えて計測が必要です。stat unit のGameは、ゲーム側の処理にかかる時間を見る手がかりになります。ただしBlueprintだけでなく、AIなども含まれます。

同じLevel・同じ体数・同じ操作で比較し、表示やログの追加による影響にも注意します。詳しくはstatコマンドの記事へ進んでください。「ノードが減ったから、必ずFPSが上がったはず」とは判断しません。

C++へ移す前に、何を繰り返しているかを見る

計算を大量に繰り返す処理は、C++で実装すると改善する場合があります。ただし、同じ相手を何度も探す設計なら、言語を変える前に、そもそもその探索が必要かを見直せます。

まず動作を確かめ、重い箇所を測り、理由に合った変更を一つ行います。全部の項目を一度に直すより、何が効いたのかを判断しやすくなります。

まとめ

Blueprintを見直すときは、ノード名から禁止事項を探すより、出ている症状から考えます。

症状見直すこと
重い・読み込みが増えるTickの頻度、一覧の作り直し、Pureの評価、具体的なClassへの依存
動きがおかしい参照先の有効性、力と速度の違い、通知が必要な期間
読みにくい処理の役割、変数の寿命、名前とコメント

実践では、目印を毎回集める処理を直接指定へ変え、消えた後は位置を読まない分岐を加えました。普段の動作が同じかと、条件が変わっても困らないかの両方を試すことが、修正を確かめる一歩になります。

原因をもう少し詳しく追いたい場合は、Blueprint Debuggerの記事で、処理を止めて値を調べる方法を試してください。

参考リンク

次に読む学習パスの次の記事
Blueprint

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

この記事を読む
Unreal Engine このセクションのノート98