【Unreal Engine】ソフト参照と非同期ロード入門:必要になってからメッシュを読み込む

作成: 2026-07-20最終更新: 2026-09-07

ハード参照とソフト参照を、読み込むタイミングの違いから整理。近づくと箱が円錐へ変わるBlueprintを作り、Async Load Assetの完了・失敗・再試行と、参照関係と実行時メモリの調べ方を図解します。

図鑑には100種類のアイテムを登録したけれど、画面に出すのは選んだ1個だけ。その1個を見るために、まだ選んでいないモデルまで読み込む必要はあるでしょうか。

こうした読み込みを整理するのが ソフト参照 です。アセットの場所を覚えておき、使うことが決まってから読み込みます。さらに 非同期ロード を使えば、完了を待つ間も、ほかのゲーム処理を進められます。

この記事では、近づくと仮の箱が円錐へ変わる、小さな展示物を作ります。まず「後から読み込み、届いた結果で見た目を変える」流れをつかみ、その後で自分のゲームの重いモデルへ応用してみましょう。

アセットの場所を覚え、必要になったら読み込んで箱を円錐へ変えるイメージ

この記事でわかること

  • ハード参照とソフト参照で、読み込むタイミングがどう変わるか
  • Async Load Assetの完了を受け取り、メッシュを差し替える手順
  • 読み込み中の重複要求と、失敗した場合の扱い
  • 参照関係と、実行中のメモリを分けて調べる方法

Sponsored

アセットの「場所」を覚えておく

参照 は、使いたい相手を指定するためのものです。たとえばStatic Mesh型の変数に円錐のアセットを設定すると、その変数から同じ円錐を指定できます。ここでいうアセットは、メッシュや画像、音など、コンテンツブラウザに保存された素材です。

ハード参照は、読み込みがつながる

Blueprintのメッシュ変数を Object Reference にし、初期値に特定のアセットを保存すると、アセットへの ハード参照 になります。そのBlueprintを読み込むと、必要な参照先も一緒に読み込まれます。

このつながりは、参照先のさらに先へ続くことがあります。展示物のBlueprintがメッシュを使い、メッシュがマテリアルを使い、そのマテリアルがテクスチャを使う、といった関係です。表示候補を全てハード参照で持つと、今は選ばない候補まで読み込む原因になります。

ハード参照は依存先も読み込み、ソフト参照はアセットの場所を記録する比較

一方、Soft Object Reference(ソフト参照) は、アセットの場所である パス を記録します。たとえば /Game/AsyncDemo/Meshes/SM_AsyncDemo という住所を覚えておき、その参照だけではメッシュを読み込みません。

比べる点ハード参照ソフト参照
アセットを保存した側を読み込むと参照先も読み込まれるその参照だけでは参照先を読み込まない
アセットを使うとき読み込まれた相手を指定するロードを要求し、結果を受け取って使う
選ぶ目安最初から使う、待たせたくないもの出番が後、または使うか未定のもの

ソフト参照は、読み込みをなくす仕組みではなく、読み込む時期を選ぶ仕組みです。 別のBlueprintやレベルが同じメッシュをハード参照していれば、そちらから読み込まれることもあります。変数1個をソフト参照にしただけで、必ず未読込になるわけではありません。

ObjectとClassは何を選ぶかが違う

今回は「円錐のメッシュ」という素材を読み込むので、Static MeshのSoft Object Referenceを使います。これに対し、後から敵のBlueprintを読み込んでSpawnしたい場合は、Soft Class Reference を使います。Classは「どの種類のActorを作るか」という設計図を指定するものです。

後から使いたいもの変数の参照の種類読み込みノード
メッシュ、画像、音などのアセットSoft Object ReferenceAsync Load Asset
SpawnするActorなどのBlueprintクラスSoft Class ReferenceAsync Load Class Asset

クラスを読み込むことと、Actorを生成することは別の処理です。後者ではロード完了後のClassを Spawn Actor from Class へ渡します。

非同期は、完了を後から受け取る

同期ロード は、読み込みが終わるまで、その場で待ってから次の処理へ進む方式です。Load Asset Blocking がこれに当たります。大きなアセットをプレイ中に読むと、1フレームが長くなり、画面が引っかかる原因になります。

非同期ロード は、読み込みを頼み、結果を後から受け取る方式です。Async Load Asset では、完了したときに Completed の実行出力から続きを動かします。

同期ロードは完了まで待ち、非同期ロードは後からCompletedを受け取る時間軸
Async Load Assetのピン役割
Asset読み込みたいソフト参照を渡す
上側の通常の実行出力(Out)完了を待たずに続ける処理。今回の実践では使わない
Completed読み込みの結果を使う処理へ進む
Object読み込めたアセットを受け取る。失敗時は有効な結果にならない

メッシュを差し替える処理は Completedの先 に置きます。上側の実行出力につなぐと、まだ結果が届いていないうちに使おうとしてしまいます。

待つ長さは、アセットの大きさ、実行環境、すでに読み込まれているかで変わります。すぐ使える場合もあれば、時間がかかる場合もあります。「何秒待つ」と決め打ちするより、Completedを合図にするのが確実です。

なお、非同期にしても処理の負荷は残ります。読み込み後に大きなモデルを初期化したり、一度に多くの表示を更新したりすれば、そこで重くなることもあります。待ち方を変えたうえで、実際の重さも測る と考えてください。

実践:近づくと箱が円錐へ変わる展示物

ここからはBlueprintのThird Personテンプレートを使います。展示物の近くに入ると読み込みを始め、成功したら箱を円錐へ差し替えます。読み込めなければ箱を残し、画面へ失敗を表示します。

近づく前は箱、ロード成功後は円錐になる展示物の完成イメージ

円錐は軽いので、目に見える待ち時間や性能差は出なくても構いません。まずは、完了の通知から正しく見た目を変更できることを確かめます。

読み込む素材を用意する

コンテンツブラウザの設定で「Show Engine Content」をオンにし、/Engine/BasicShapesCone を見つけます。これをプロジェクト側の Content/AsyncDemo/Meshes へコピーし、SM_AsyncDemo と命名してください。フォルダへドラッグしたときは、移動ではなくコピーを選びます。

この複製を使うと、後でReference Viewerや計測結果を調べるとき、自分の検証用メッシュを名前で探せます。SM_AsyncDemo をレベルへ直接置く必要はありません。

展示物と、近づいたことを調べる範囲

親クラスをActorにして BP_AsyncDisplay を作ります。Components欄で、DefaultSceneRootの子に次の2つを追加してください。

名前コンポーネント設定
DisplayMeshStatic MeshMesh = /Engine/BasicShapes/Cube、Location = (0, 0, 50)、Mobility = Movable、Collision Presets = NoCollision
LoadZoneBox CollisionLocation = (0, 0, 100)、Box Extent = (200, 200, 150)、Collision Presets = OverlapOnlyPawn、Generate Overlap Events = オン
BP_AsyncDisplayのコンポーネント階層とLoadZoneの大きさ

DisplayMeshは、画面に形を出す部品です。最初はCubeを割り当て、読み込みが済むまでの仮表示にします。Movable はゲーム中に変更できる設定、Overlap はぶつかって止める代わりに「範囲が重なった」と通知する仕組みです。

Box Extentは箱の中心から各面までの距離です。今回の範囲は幅400cm・奥行き400cm・高さ300cmになります。範囲に入る前の箱も見えるよう、Player Startから少し離して配置します。

ソフト参照と、2つの状態を用意する

「My Blueprint」で次の変数を作ります。TargetMeshは型の選択で Static Mesh を検索し、参照の種類を Soft Object Reference にしてください。Static Mesh Componentとは別の型です。

Static MeshのSoft Object Referenceを選び、SM_AsyncDemoを初期値にする設定
変数名初期値意味
TargetMeshStatic MeshのSoft Object ReferenceSM_AsyncDemo後から読み込むメッシュの場所
bBusyBooleanfalse今、読み込みを待っている
bAppliedBooleanfalse読み込んだメッシュを適用できた

変数を作ったらCompileし、TargetMeshの初期値でSM_AsyncDemoを選びます。今回は1種類だけ試すので、Instance Editableはオフのままで構いません。

DisplayMeshに最初からSM_AsyncDemoを割り当てない こともポイントです。TargetMeshをソフト参照にしていても、表示用コンポーネントへ直接設定すれば、そちらがハード参照になります。

Sponsored

近づいたら、読み込みを頼む

Event Graphで右クリックし、「Add Custom Event」から RequestDisplayMesh を作ります。入力は不要です。このイベントが、読み込み処理の入口になります。

今回は Event Graph上のCustom Event で組みます。Async Load Assetのように完了を後から受け取る処理を、通常のFunctionの中へ入れようとしないでください。

プレイヤーが入ったときだけ呼ぶ

LoadZoneを選び、Detailsのイベント欄から On Component Begin Overlap を追加します。Other Actorを Get Player Character(Player Index = 0)のReturn Valueと Equal (Object) で比べ、結果をBranchのConditionへつなぎます。

Other ActorとGet Player Characterの結果をEqual Objectで比較する接続

同じOverlapイベントからBranchへ白い実行線をつなぎ、True側から RequestDisplayMesh を呼びます。先ほどのEqual (Object)のReturn Valueを、このBranchのConditionへつなぎます。TargetはSelfです。Selfは「このBP_AsyncDisplay自身」を指します。False側は何もつなぎません。

OverlapからBranchへ進み、一致したらRequestDisplayMeshを呼ぶ接続

これで、テンプレートのプレイヤーが入ったときだけ、自分の読み込みイベントへ進みます。今回は1人用の実験です。イベント名と同じ名前の呼び出しノードを検索して置き、赤いCustom Eventの入口と区別してください。

読み込み中と、適用済みを除く

RequestDisplayMeshの入口からBranchを置きます。bBusyとbAppliedのGetを OR Boolean へ入れ、その結果をConditionへつなぎます。

bBusyとbAppliedのどちらかがtrueかをOR Booleanで調べる接続

OR は、どちらかがtrueならtrueを返します。読み込み中、または適用済みなら、BranchのTrue側で何もせず終えます。False側を Set bBusy(true)へつなぎ、続いて Print StringLoading... と表示します。

BranchのFalseからbBusyをtrueにしLoadingを表示する接続

bBusyは、待っている間に同じ要求を重ねないための印です。bAppliedは、成功した後に同じ差し替えを繰り返さないための印です。「頼んだ」と「できた」を別々に記録しておけば、失敗した場合の扱いも分かりやすくなります。

届いたメッシュを適用する

Print Stringの後に Async Load Asset を置き、TargetMeshのGetをAssetへ接続します。上側の通常の実行出力は使わず、Completedから Set bBusy(false)へつないでください。

TargetMeshをAsync Load Assetへ渡し、CompletedでbBusyをfalseにする接続

Completedへ進んだら、その要求の待ち時間は終わりです。ただし、それだけで 目的のメッシュを使えるとは限りません。 結果を調べてから差し替えます。

成功した場合は、表示用の部品へ渡す

Cast to Static Mesh を追加します。Async Load AssetのObjectをCastのObjectへ、Set bBusyの実行出力をCastの実行入力へつなぎます。

ここでの Cast は、「受け取った相手をStatic Meshとして使えるか」という確認です。別のメッシュへ変換したり、このノードがアセットを新たに読み込んだりするわけではありません。結果が空ならCast Failedへ進みます。

ロード結果をCast to Static Meshで確認し、失敗時はLoad failedを表示する接続

Cast成功側の実行出力から Set Static Mesh を呼びます。Components欄のDisplayMeshをグラフへドラッグし、その参照をTargetへ。CastのAs Static Meshは、New Meshへつないでください。

DisplayMeshのTargetとロード結果のNew Meshを分けて接続し、適用済みにする流れ

Targetは「見た目を変える部品」、New Meshは「その部品に表示させる素材」です。どちらも青い線ですが、指定する相手が違います。

Set Static Meshの後に Set bApplied(true)、さらに Print StringReady)をつなぎます。DisplayMeshが読み込んだアセットを使い続けるので、表示に必要な参照もここで保持されます。

失敗した場合は、箱を残す

Cast Failedは Print String へつなぎ、Load failed と表示します。Set Static MeshもSet bAppliedも通りません。最初の箱が残り、bBusyとbAppliedは両方falseになります。

これならプレイヤーが一度範囲を出て、再び入ったときに読み込みを試せます。ただし、アセットを未設定にしたままでは、試し直しても失敗します。原因を直す必要があります。

処理全体は次の形です。分割した図は、全て同じEvent Graphの続きです。

RequestDisplayMesh(Custom Event)
  → Branch(bBusy OR bApplied)
      True  → 何もしない
      False → Set bBusy(true)
            → Print String(Loading...)
            → Async Load Asset(Asset = TargetMesh)
                通常の実行出力 → 未接続
                Completed → Set bBusy(false)
                          → Cast to Static Mesh(Object = ロードのObject)
                              成功 → Set Static Mesh
                                       Target = DisplayMesh
                                       New Mesh = As Static Mesh
                                   → Set bApplied(true)
                                   → Print String(Ready)
                              Cast Failed → Print String(Load failed)

成功と失敗を確かめる

BlueprintをCompileして保存し、BP_AsyncDisplayをレベルへ1個置きます。床の上に箱が見え、Player StartがLoadZoneの外になるようにしてください。距離は500cmほど離すと試しやすくなります。

  1. Playし、まず箱が見えることを確認します。
  2. 箱へ近づきます。Loading... の後に Ready が出て、箱が円錐に変われば成功です。
  3. 範囲から出て、もう一度入ります。適用済みなので、Loading...を繰り返さないことを確認します。
  4. Playを止め、TargetMeshの初期値を None にしてCompileします。もう一度Playして近づくと、Load failed が出て箱が残ることを確認します。
  5. 再び範囲を出入りし、失敗後にも要求できることを確認します。最後にTargetMeshをSM_AsyncDemoへ戻して保存します。

小さな円錐や読込済みの素材なら、ほとんど待たずに差し替わります。それでも問題ありません。差し替えが見えたことは、Completedから表示を更新できた確認です。未読込だったことや、省メモリの証明とは分けて考えます。

成功するとLoading...のあと箱が円錐へ変わり、失敗するとLoad failedが出て箱のまま残る
症状確認する場所
最初から円錐になっているDisplayMeshの初期メッシュ。ハード参照だけで見た目が勝手に変わるわけではない
Loading...が出ないLoadZoneのOverlap設定、プレイヤー側のGenerate Overlap Events、Player Startが範囲外か
Loading...の後に進まないCompletedからSet bBusyへつながっているか
Load failedになるTargetMeshの初期値、アセットが存在するか、パッケージへ含まれているか
Readyは出るが見た目が変わらないSet Static MeshのTargetがDisplayMeshか、New MeshがCastの結果か、MobilityがMovableか
2回目以降に失敗を試せない新しいPlayで試しているか。成功後はbAppliedで要求を止める例になっている

Overlapの調べ方はコリジョンの記事、ログの読み方はPrint StringとOutput Logの記事も参考になります。

Sponsored

本当に読み込む量が減ったか調べる

ここからは、完成したゲームへ応用するときの確認です。先ほどの小さな円錐だけでは、差が小さくても自然です。確認する道具を、答えてくれる疑問で分けます。

参照関係、依存先の�サイズ、実行中のメモリを別々に調べる道具

参照のつながりを調べる

BP_AsyncDisplayを右クリックし、Reference Viewer を開きます。これは、アセットが何を参照し、何から参照されているかをたどる道具です。Hard ReferencesとSoft Referencesの表示を切り替え、SM_AsyncDemoへの関係を確認します。

今回、TargetMeshからはソフト参照、DisplayMeshの初期値からはCubeへのハード参照になるはずです。SM_AsyncDemoが別のレベルやBlueprintからもハード参照されていれば、そこから読み込まれる経路が残っています。

同じ右クリックメニューの Size Map は、選んだアセットと依存先のサイズを図で比較する道具です。読み込む候補の大きさを調べる助けになりますが、今この瞬間のゲーム内メモリを表示しているわけではありません。 表示範囲や参照の種類も確かめて読みます。

実行中のメモリは、別に測る

メッシュをエディタで開いたり、プレビューしたりすると、すでに読み込まれている場合があります。実行時の効果を調べるなら、Development設定でパッケージ化したゲームを起動し直して比較すると、エディタ側の参照を避けられます。

  1. 検証レベルを含むDevelopment版を作り、起動します。
  2. 展示物の範囲へ入る前にコンソールで Memreport -full を実行します。
  3. 範囲へ入り、Readyを確認してから、もう一度同じコマンドを実行します。
  4. ゲーム側の Saved/Profiling/Memreports に保存されたレポートを開き、メッシュや関連テクスチャの内訳と合計を比べます。保存場所は実行環境によって異なります。

レポート作成自体にも負荷があるため、この最中の引っかかりをロード性能と混同しないでください。また、総メモリだけの増減を全てSM_AsyncDemoの影響と決めつけず、対象の内訳や他の参照も調べます。

読み込み時の引っかかりが気になる場合は、Unreal Insightsで、その時間帯の処理を記録します。参照のつながり、使ったメモリ、フレームの重さは、それぞれ確認する対象が違います。

使う少し前に読み込み、不要になったら手放す

実践では分かりやすくするため、展示物へ近づいてから要求しました。ゲームへ組み込むなら、見せる瞬間より少し前 に読み込みを始めると、待ち時間を目立ちにくくできます。

展示物へ到着する少し前の通路で読み込みを要求する例

たとえばボス部屋の手前の通路で要求し、到着までに準備を進めます。図鑑なら、次に選ばれそうな数個のモデルを先に読み込む方法もあります。全候補を先読みすると保持する量がまた増えるので、待ち時間とメモリの両方から範囲を決めます。

逆に、プレイヤーの標準メッシュや基本UIなど、最初から使うものはハード参照で準備しておく方が単純です。「全てソフト参照にする」ことを目標にせず、出番を遅らせられる素材から検討します。

参照を手放すとは

読み込んだメッシュは、DisplayMeshが使っている間は保持されます。不要になったら、表示を別のメッシュへ戻す、展示物自体を破棄するなどして、その使用を終えます。

表示用部品が参照を手放しても、別の展示物が使っていれば保持される関係

ほかの変数や別の展示物も同じアセットを使っていれば、まだ残ります。不要な強い参照がなくなったものは、後の ガベージコレクション(GC) で回収の対象になります。GCは、使わなくなったものを片付ける仕組みです。変数を空にした瞬間、使用メモリが同じ量だけ減るとは限りません。

ソフト参照は、アセットを使い続けるための保持ではありません。今回のように表示用コンポーネントへ渡すか、後で使うなら通常のObject Reference変数などに結果を保存します。

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

参照を取得することと、読み込むことは別です。 読み込み済みの相手を探すだけの操作では、未読込なら空の結果になります。ソフト参照からの変換を全て「自動で同期ロードする」と考えず、実際にロードするノードを確認してください。プレイ中の待ちを避けたい箇所では、Load Asset Blockingの代わりにAsync Load Assetで完了を受け取ります。

Skeletal Meshへ広げるときは、アニメーションも確認します。 動く敵の外見を変える場合は、メッシュだけでなく、SkeletonやAnimation Blueprintの対応が必要です。この記事はロードの流れに集中するため、Static Meshを使いました。

画面を切り替えるUIでは、届く順番にも注意します。 アイテムAを要求した直後にBを選ぶと、Aの結果が後から届くことがあります。適用する前に「今もこのアイテムを表示したいか」を確認し、古い結果で新しい選択を上書きしないようにします。今回の例は1種類固定なので、この管理を省いています。

アセット数が増えたら、Asset Managerを検討します。 読み込むまとまりや保持・解放を管理する仕組みです。Data Assetの記事で紹介するPrimary Data Assetはその土台にもなりますが、作るだけで非同期になるわけではありません。まずは今回の1個を読み込む形で十分です。

ソフト参照は、配布物へ入れる設定の代わりではありません。 プロジェクトにある素材でも、パッケージへ含まれていなければ製品版では読み込めません。特に文字列から動的にパスを組む場合は、Cook(実行用データへの変換)の対象も確認します。

レベル全体の読み込みは別の単位で考えます。 Async Load Assetでメッシュを読めても、Open Levelの切り替えがそのまま非同期になるわけではありません。エリアを段階的に読み込む話はレベルストリーミングとWorld Partitionへ進んでください。

まとめ

ソフト参照は、アセットの場所を覚え、必要な時期に読み込むための仕組みです。実践では、近づいたらAsync Load Assetへ要求し、Completedで結果を確認してから箱を円錐へ差し替えました。

最初は1個の素材で、成功時・失敗時・読み込み中の動きを揃えてみてください。そのうえで、Reference Viewerでつながりを調べ、実行中のメモリと重さを測ると、「どの素材を後回しにすると効くか」を判断できます。

素材の読み込みではなく、同じActorを何度も作って消す処理が重い場合は、オブジェクトプーリングも検討できます。

参考:非同期アセット読み込みAsync Load AssetReference ViewerSize Mapとメモリレポート

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