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

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

Blueprintのつまずきを「重い・動きがおかしい・読めない」の3つから調べます。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:名前とコメントから、目的が分からない

Value1Value2 では、何を比べるのか伝わりません。現在の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へ MarkerActorの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の記事で、処理を止めて値を調べる方法を試してください。

参考リンク

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