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

作成: 2025-12-12最終更新: 2026-07-19

Tickの多用、Castの乱用、Get All Actors Of Classの連打——Blueprintでありがちな10のミスを「重い・壊れる・読めない」の3グループに整理し、健康診断として自分のBlueprintを1本直すところまで。

Blueprintは動いてしまうのが良いところであり、こわいところでもあります。「とりあえず繋いだら動いた」まま3ヶ月進み、気づけばフレームレートが落ち、たまにクラッシュし、そして自分で書いたはずのグラフが読めない。多くの人が同じ順番でこの壁に当たります。

つまずき方はだいたい決まっています。この記事では、Blueprintでありがちな10のミスを「 重くなる/壊れやすくなる/読めなくなる 」の3グループに整理し、それぞれ「なぜ起きるか」と「代わりにどうするか」をセットで解説します。最後は、自分のBlueprintを1本開いて健康診断し、その場で2箇所直します。

10のミスを、重い・壊れる・読めないの3つの引き出しに整理するイメージ

この記事でわかること

  • ミスは3グループに整理できる: 重い(性能)・壊れる(安定性)・読めない(保守性)
  • 「重い」の三大巨頭: Tick・Get All Actors Of Class・Pureノードの再計算
  • Castの本当の問題は速度ではなく ハードリファレンスによるメモリの道連れ
  • IsValidUnbind Event という、 クラッシュを防ぐ2つの習慣
  • 実践:自分のBlueprintを 健康診断して2箇所直す

Sponsored

なぜ同じミスを踏むのか

10個のミスには共通の背景があります。 Blueprintは「今すぐ動かす」ための最短ルートを、いつも目の前に用意してくれます

毎フレーム何かしたい → Event Tick に繋げば動く。あのActorを触りたい → Get All Actors Of Class で拾えば動く。相手の関数を呼びたい → Cast To すれば動く。どれも間違いではありません。 1体・1回・1箇所なら正解 です。

問題は、その最短ルートが 「数が増えたとき」「時間が経ったとき」に別の顔を見せる ことです。敵が1体から100体になった瞬間、レベルが1つから20個になった瞬間、あるいは3ヶ月後に自分がそのグラフを開いた瞬間に、コストが表に出てきます。

だから10のミスは、覚えるべき禁止事項ではなく、 「これは数と時間に耐えるか?」という問いのチェックリスト として読んでください。


グループ1:重くなるミス

まずは性能に効くもの。フレームレートが落ちる原因はレンダリングや物理にもありますが、 Blueprintが原因のとき はたいていこの3つのどれかです。

3つに共通しているのは 「必要になるたびに、外へ探しに行く」 という形です。

毎回外へ探しに行く人形と、手元にカードを1枚持っている人形の対比図

ミス1: とりあえずEvent Tickに繋ぐ

Event Tick は毎フレーム実行されます。60FPSなら1体あたり毎秒60回。敵を100体並べれば 毎秒6000回 です。1回が軽くても、掛け算が効いた瞬間に重くなります。

代わりにどうするか: 「常に見張る」のをやめて「起きたときだけ動く」設計へ切り替えます。定期処理は Set Timer by Event 、状態の変化は Event Dispatcher 、接触は Collision Event に任せる。ここで効くのは「Timerに変えたこと」自体ではなく、 実行回数を毎秒60回から数回へ落とせること です。同じ頻度で同じ処理を回すなら、Timerにしても軽くはなりません。どうしてもTickが要るなら、Tick Intervalで頻度を落とすか、Set Actor Tick Enabled で必要なときだけ起こします。

具体的な建て直し方は Event TickをやめてTimerとイベントで設計する方法 で、見張りAIを丸ごと組み直しながら解説しています。

ミス2: Get All Actors Of Class を毎回呼ぶ

このノードは、ワールド内の すべてのActorを走査して 該当クラスを集めます。レベルが大きくなるほど重くなる処理で、これをTickの中や頻繁なイベントで呼ぶのは、電話帳を最初から最後まで読み上げてから電話をかけるようなものです。

代わりにどうするか: Event BeginPlay一度だけ 取得して変数に保持します。プレイヤーが相手なら Get Player Character が最短です(ただし返るのはCharacter派生のPawnだけなので、独自のPawnを使っているなら Get Player Pawn を使います)。複数を対象にするなら、探す側ではなく 生まれた側から名乗り出る 設計にします。スポーン時にマネージャーの配列へ自分を登録しておけば、数が増えても破綻しません。

ミス3: Pureノードの再計算に気づかない

Blueprintのノードには、実行ピン(白い三角)を持つ Impureノード と、持たない Pureノード があります。Pureノードは値を保存せず、 出力ピンが参照されるたびに計算し直します

重い計算をするPureノードの出力を3箇所に繋いだら、その計算は3回走ります。見た目は1つのノードなので、これは気づきにくいミスです。

代わりにどうするか: 重い計算のPureノードは、結果を 一度変数にSetしてから 使い回します。このとき「いつSetするか」を決めておくのが大事で、 そのイベントの中で使い回すだけ なのか、 状態が変わったときに更新する のかを曖昧にすると、今度は古い値を使い続けるバグになります。

なお、Pureであること自体が悪いわけではありません。Pureは「副作用がなく値を返すだけ」の印であり、重い処理でも 1箇所からしか繋がない・低頻度でしか通らない なら問題ありません。気をつけるべきは 出力を複数箇所へ分岐させたとき です。


Sponsored

グループ2:壊れやすくなるミス

次は安定性。クラッシュや「たまに動かない」の原因になるものです。原因は大きく2つ—— 相手がもういない か、 相手を知りすぎている かです。

もういない相手に手を伸ばす人形と、UIから敵・メッシュ・テクスチャが鎖でつながる図

ミス4: Cast To を配りすぎる

意外に思われますが、 Cast処理そのもののCPU負荷はごくわずか です。問題になるのは ハードリファレンス のほうで、Cast To BP_Enemy と書いた時点で、そのBlueprintは「BP_Enemyを知っている」状態になり、BP_Enemyが抱えるメッシュ・テクスチャ・アニメーションが 道連れでメモリにロードされます

UIのウィジェットが敵をCastし、敵がボスをCastし……と連鎖すると、たった1つのUIを開くために大量のアセットが読み込まれます。起動時間とメモリ使用量が膨らむのはこれが原因です。

代わりにどうするか: 相手の型を知らずに用件だけ伝える Blueprint Interface 、または誰が聴いているか知らずに放送する Event Dispatcher を使います。

判断基準は Blueprint Interfaceの記事通信3方式の総論 にまとめています。

ミス5: 参照の有効性を確認しない

「敵を倒したあとにUIが敵のHPを読みに行く」——これで落ちます。参照していたActorがすでに破棄されているのに、そのまま使おうとしたからです。

代わりにどうするか: Nullになりうる参照 を使う直前に Is Valid を挟みます。すべての参照の手前に機械的に置く必要はなく、狙うのは「破棄されうる相手」「取得に失敗しうる相手」——つまり 他のActorへの参照実行時に取得した参照 です。

もう1つ知っておきたいのが、 Is Valid は止めるだけで直してはくれない こと。プレイヤーがリスポーンした場合、BeginPlay で保存した参照は無効になったままなので、取り直す仕組みが別に必要です。

ミス6: 物理演算を毎フレーム叩く

Add ForceSet Physics Linear Velocity をTickで呼び続けると、負荷に加えて 挙動が読めなくなる という別の問題が生まれます。物理エンジンの計算に、こちらの入力が毎フレーム割り込む形になるからです。

代わりにどうするか: キャラクターの移動は Character Movement Component に任せます。物理を直接触るのは、爆発の吹き飛ばしのように 瞬間的な一撃 のときだけにします。

ミス7: Event Dispatcherのバインドを外し忘れる

Event Dispatcherに Bind Event したまま、聴いている側のActorが破棄されると、破棄済みの相手にイベントが飛ぶことになります。 不定期なクラッシュ の典型的な原因です。

代わりにどうするか: Event BeginPlay でBindしたら、 Event EndPlayUnbind Event します。 Bindを書いたら、その場でUnbindも書く 。ペアで書く癖をつけると忘れません。

Bind/Unbindの書き方は Event Dispatcherの記事 を参照してください。


グループ3:読めなくなるミス

最後は保守性。 3ヶ月後の自分に効いてくる グループです。

1枚に全部詰めて線が絡まったカードと、移動・攻撃・被弾に分かれた3枚のカードの対比図

ミス8: 1つのグラフに全部詰める

1つのイベントに数十のノードがぶら下がり、ワイヤーが交差しはじめると、そのグラフはもう読むものではなく 解読するもの になります。

代わりにどうするか: 処理のまとまりごとに Function へ切り出します。ノードを囲むだけの Collapse Graph は整理にはなりますが、再利用もテストもできないので、まずFunction化を狙ってください。

ただし1つ制約があります。 Functionの中には DelayAI Move To のようなLatentノード(完了までに時間がかかるノード)を置けません 。そこを切り出したいときはMacroかCollapsed Graphを使います。

切り出す単位の決め方は Blueprintグラフを整理する10のテクニック 、FunctionとMacroの使い分けは FunctionとMacroの違いと使い分け にあります。

ミス9: 変数を全部Variablesパネルに置く

ループの合計値や一時的な計算結果まですべてClass Variableにすると、変数リストが膨れ上がるだけでなく、 前回の値が残っていて2回目がおかしくなる という実害が出ます。

代わりにどうするか: 関数の中だけで使う値は Local Variable に置きます。呼び出しのたびに初期値から始まるので、リセット漏れという概念自体がなくなります。

実際に「2回目だけ合計が2倍になる」バグを再現して直す手順が Class変数とLocal変数の使い分け にあります。

ミス10: コメントも命名もしない

Blueprintは絵なので、 絵を見れば分かるはず と油断しがちです。しかし数週間後に開くと、なぜこの分岐があるのかは絵からは読み取れません。

代わりにどうするか: 処理のまとまりを Comment Box で囲み、 「何をしているか」ではなく「なぜそうしているか」 を書きます。Booleanは bIsDead のように b で始める、Interfaceは BPI_ で始めるなど、命名を揃えるだけでも一覧の見通しが変わります。

Sponsored

実践:自分のBlueprintを健康診断する

ここまでを読むだけでは何も直りません。 いま作っているプロジェクトのBlueprintを1本開いて 、その場で診断しましょう。メトロイドヴァニアの敵、タワーディフェンスの砲台、ホラーゲームの徘徊する何か——役割は違っても、症状はだいたい同じです。

ここでは、多くの人が最初に書く形の敵Blueprintを題材にします。

再現用の準備: BP_Enemy に次の変数があるものとします。

変数名初期値役割
ChaseRangeFloat800.0追跡を始める距離
PlayerRefActorなしプレイヤーの参照(今は持っていない)

型を BP_Player ではなく Actor にしているのには理由があります。 変数の型として BP_Player を指定した時点で、ミス4で説明したハードリファレンスが発生する からです。今回は AI Move To の行き先として渡すだけなので、Actor で足ります。

加えて、敵側に次を用意しておきます。

  • DetectionSphere(Sphere Collision Component・Radius 800)を追加し、詳細パネルで Generate Overlap Events にチェック。Collision Presetは Custom にして Pawnチャンネルだけ Overlap 、他は Ignore にします(OverlapAllDynamic にすると弾や小物にも反応してしまいます)
  • AI Move To を動かす前提: 敵がPawn/Character派生であること、AI Controller Classが設定されていて実際にPossessされていること、そして移動範囲を覆う Nav Mesh Bounds Volume がレベルに置かれていること(→ NavMeshの設定

診断前のグラフは、次の形をしています。 3つの症状が同時に出ている のが分かるでしょうか。

診断前の敵Blueprint。Event TickからGet All Actors Of Class、Cast To BP_Player、距離判定が毎フレーム走るノードグラフ
Event Tick
  → Get All Actors Of Class(Actor Class: BP_Player)   ← ミス2:毎フレーム全走査
  → Get(Index 0)
  → Cast To BP_Player                                   ← ミス4:ハードリファレンス
  → Get Distance To(Target: Cast結果)
  → Branch(Distance < ChaseRange)
      True → AI Move To(Goal Actor: Cast結果)

診断結果: 毎フレームの全Actor走査(ミス2)、毎フレームのCast(ミス4)、そもそも毎フレーム見張っていること(ミス1)。さらに、条件を満たしている間ずっと AI Move To を投げ続けているのも見逃せません。移動要求は1回出せば目的地まで走ってくれるので、毎フレーム出し直す必要はありません。

直し方は3手です。

  1. 探すのをやめる: Event BeginPlayGet Player Character を1回だけ呼び、PlayerRef に保持します。以降どこからでもこの変数を使えます
  2. 見張るのをやめる: Event Tick の距離判定を捨て、DetectionSphereOn Component Begin Overlap に置き換えます。近づいてきたら向こうから教えてくれます
  3. 相手を確かめてから動く: Overlapは プレイヤー以外でも発火しうる ので、飛び込んできたActorが PlayerRef と同一かを比べます。そのうえで Is Valid を通します

直したあとのグラフはこうなります。

診断後の敵Blueprint。BeginPlayで一度だけプレイヤー参照を取得し、Overlapで追跡を開始するノードグラフ
Event BeginPlay
  → Get Player Character
  → Set PlayerRef                                       ← 探索は起動時の1回だけ
  → Set Actor Tick Enabled(false)

On Component Begin Overlap(DetectionSphere)
  → Is Valid(PlayerRef)
      Valid → Branch(Condition: Other Actor == PlayerRef)   ← 入ってきたのは本当にプレイヤーか
          True → AI Move To(Pawn: Self, Goal Actor: PlayerRef)

Playして、敵から離れた場所に立ってみてください。 こちらが検知範囲に入るまで敵は一切動かず、範囲に入った瞬間だけ追跡を始める 。それが見えれば成功です。

思ったとおりに動かないときは、On Component Begin OverlapPrint String を1つ挿して切り分けます(→ Print Stringで値を確認する)。

  • ログが出ない → Overlapが発火していません。DetectionSphereGenerate Overlap Events とCollision設定を確認
  • ログは出るが動かない → AI側です。AI Controllerが割り当たっているか、AI Move ToPawn にSelfが繋がっているか、レベルにNav Mesh Bounds Volumeがあるか
  • 最初から追ってくる → 半径が大きすぎるか、Pawn以外にも反応しています

ポイントは2つです。

  • 探しに行くのをやめると、複数のミスが同時に消える: 「毎フレーム探す」をやめただけで、ミス1・2・4がまとめて解決しました。 参照は先に持っておく、きっかけは向こうから来てもらう 。この2つが、グループ1の大半に効く共通の直し方です
  • 見た目の確認と、負荷の計測は別物: 「敵が止まっている」で分かるのは設計が変わったことだけ。どれだけ軽くなったかは stat unit で測ります(→ statコマンドで測る)。効果を実感したいなら、Alt +ドラッグで敵を30体に増やしてから、直す前と後で stat game の数字を比べてください。1体では誤差でも、数が増えるとTick版だけ数字が伸びていきます

Castまで完全に外したい場合は Blueprint Interfaceの記事 へ、値の中身を追いたくなったら ブレークポイントで止める方法 へ進んでください。


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

「BlueprintよりC++が速い」は、正確には半分だけ本当です。 Blueprintのノードは仮想マシン上で1つずつ解釈されて実行されるため、 ノードを渡り歩くこと自体 にオーバーヘッドがあります。ただし AI Move To のような組み込みノードの中身はC++で書かれていて、そこはフルスピードで動きます。つまりBlueprint特有の遅さは「 細かいノードを大量に踏むループ 」に出ます。とはいえ AI Move To やActorの列挙のように、 中身の処理そのものが重い ノードもあるので、「組み込みノードだから安心」とは限りません。数万回まわる計算や毎フレームの複雑な数学を書きたくなったときが、C++を検討するタイミングです。逆に言えば、この記事の10項目を直すほうが、C++化よりずっと大きく効くケースがほとんどです。

ミスを「見つける」手段も持っておきましょう。 体感で重い気がする、では直す場所を間違えます。エディタで `(バッククォート)を押してコンソールを開き、stat unit と打てばFrame / Game / Draw / GPU の内訳が出ます。Gameの数字が大きければ ゲームスレッドが詰まっている サインです。ただしGameにはAI・アニメーション・物理・UIも含まれるので、そこから先は stat game などで絞り込みます。Blueprintが濃厚だと感じたら、この記事のグループ1から当たってください。

全部を一度に直そうとしないでください。 10項目の中で、いま効くのは1つか2つです。まずは stat unit で当たりをつけ、そのグループだけ直す。動くものを壊さないことも、立派なベストプラクティスです。


まとめ

10のミスは、覚える順番よりも どのグループの症状が出ているか で引き出せると役に立ちます。

グループ症状主なミス合言葉
重くなるFPSが落ちる・カクつくTick多用 / Get All Actors Of Class / Pure再計算探しに行かず、持っておく
壊れやすい落ちる・たまに動かないCast乱用 / IsValid不足 / 物理連打 / Unbind忘れ使う前に確認、Bindしたら外す
読めなくなる改造がこわい巨大グラフ / 変数の置き場所 / 無コメント3ヶ月後の自分に説明する

共通するのは 「1体・1回・1箇所なら正解」の書き方が、数と時間に耐えるとは限らない ということです。

いま開いているBlueprint、敵が100体に増えても同じ形で戦えそうですか?