こんにちは、エンジニアのオオバです。
Resourcesフォルダ、つい何でも入れたくなりますよね。
Resources.Load で文字列から読めるので、とにかく便利です。
僕も開発中の「勇者のどうぐ屋さん」で、バトル演出のアセット一式がResources配下に置かれているのに気付きました。
しかもプロジェクトのルールでは、そこに置かない決まりの場所です。
本記事では、ルール違反の場所からアセットを移した話と、その流れで見えてきた「Resourcesに何を置くべきか」の答えをまとめます。
アセットを移動するときの落とし穴も2つ紹介します。
Resourcesに置くのは「パスで読む入口」だけでいい
先に結論です。
Resourcesフォルダの中身は、使っていなくても全部ビルドに入ります。
一方、Resourcesの外にあるアセットも、ビルド対象のシーンやResources内のアセットからGUIDで参照されていれば、依存として自動でビルドに含まれます。
つまり、Resourcesに必要なのはパス文字列で読む「入口」のアセットだけ。
入口から参照される中身は、Resourcesの外に置いて大丈夫です。
- Resources配下は使っていなくても全部ビルドに入る
- GUIDで参照されているアセットはResources外でもビルドに含まれる
- Resourcesに残すのはResources.Loadで読む入口だけ
- 移動はAssetDatabase.MoveAssetでGUIDを保ったまま行う
- SaveAssetsで無関係なdirtyアセットが混ざらないか差分を確認する
Resourcesに紛れ込んだエフェクト
バトル演出をNovaShaderへ移行した区切りで、プロジェクトの中を見直していました。
すると、エフェクト一式が次の場所に置かれていました。
Assets/Project/Resources/Battle/Timeline/
プロジェクトのルールでは、運用アセットは Assets/AssetResources 配下に置きます。
その中を RawData と Resources に分ける構成です。
「エフェクトのアセットがいつの間にかAssets/Project/Resources配下になっているけど、これはルール違反」
見つけたときの僕の正直な感想がこちら。
犯人は規約の短縮表記
原因の1つは、規約の構成表にありました。
構成表で「Resources/」と短縮して書いていたので、どのResourcesフォルダを指すのか曖昧だったのです。
最近はAIと一緒に開発しています。
AIは、規約が曖昧なら曖昧な方へ素直に置きます。
指示どおりに動いた結果なので、AIを責めても仕方ありません。
そこで構成表をフルパスで書き直し、「エフェクトはAssetResourcesに置く」と明記しました。
AIに渡す規約ほど、省略せずに書く必要がありますね。
MoveAssetならGUIDは切れない
まずは、ルールどおりの場所に移しました。
移動先は次のフォルダです。
Assets/AssetResources/Resources/Battle/Timeline/
移動には AssetDatabase.MoveAsset を使っています。
エディタのAPI経由で動かすので、GUIDが保たれたまま移動できます。
シーンやPrefabからの参照も切れません。
移動元も移動先もResourcesフォルダなので、Resources.Load に渡すパスは変わりません。
コードの変更はほぼゼロで済みました。
参照ゼロは機械判定で消す
ついでに、NovaShader化前に作った試作VFXも整理しました。
全アセットを対象に、GUIDの被参照数を数えて、参照ゼロだった7ファイルを削除しています。
目で見て「たぶん使っていない」と判断すると、消してから困ることがあります。
被参照数を数えて機械的に判定したので、安心して消せました。
一方、旧スプライト演出の37個は本編でまだ使っています。
未移行のIDが29個あるので、こちらは残しました。
本当に必要なのは3つだけ
移動が終わったあと、移した先のフォルダで Resources.Load されているアセットを調べました。
結果は、次の3つだけです。
BattleStageBattleStageOutputBattleSequenceCatalog
残りのアセットは、パスで読まれていませんでした。
- Timeline(.playable)
- VFXのPrefab / Material / Mesh
- VolumeProfile
これらは、カタログやStageからGUIDで参照されているだけです。
だとすると、Resourcesに置いておく理由がありません。
残りはRawDataへ追い出す
GUID参照だけのアセットを、次の場所へ移しました。
Assets/AssetResources/RawData/Battle/Timeline
今回もuLoop経由で AssetDatabase.MoveAsset を呼んでいます。
gitでは全件が100%リネームとして記録されました。
あわせて、周辺のツールも追従させています。
- エディタの生成ツールに
RawRootを追加し、Resources側のRootにはパスで読む3つだけを残す - 管理画面のプレビュー探索パスを変更
preview.jsonを更新
エディタツールのルート定数を、Resources用とRawData用で分けたのがポイントです。
ツールが新しく生成するアセットも、最初から正しい場所に置かれるようになります。
外に出してもビルドに入る理由
ここで「Resourcesの外に出したら、ビルドに入らなくなるのでは?」と不安になりますよね。
僕も最初は少し身構えました。
Unityのビルドに入るアセットは、大きく2種類あります。
- Resourcesフォルダ配下のアセット(使っていなくても全部入る)
- ビルド対象のシーンや、Resources内のアセットからGUIDで参照されているアセット(依存として自動で入る)
今回の構成に当てはめると、次のようになります。
BattleSequenceCatalog などの入口はResourcesにあるので、ビルドに入ります。
入口がGUIDで参照しているTimelineやVFXは、依存としてビルドに入ります。
つまり、入口さえResourcesにあれば、中身はどこに置いても届くわけです。
Resourcesに必要なのは、パス文字列で読む「入口」だけでした。
「動くからいい」ではダメな理由
「ビルドに入るなら、Resourcesに置いたままでも動くのでは?」
と思うかもしれません。
実際、動作としては何も困りません。
それでも僕がRawDataへ出したのは、次の2つが理由です。
「Resourcesフォルダに無駄にファイルがあると、今後Addressablesに移行したくなった時に困るし、そもそも起動時のルックアップテーブルを作る時間が増えちゃうので良くない」
起動時間が延びる
Resources配下のアセットは、パスで引けるようにするためのルックアップの対象になります。
ファイルが多いほど、アプリ起動時にルックアップテーブルを作る時間が増えてしまい、ユーザーに不利益が生じます。
パスで読まないアセットまでResourcesフォルダに入れておくことは全く意味がないのです。
Addressables移行がつらくなる
将来Addressablesに移行したくなったとき、Resources配下が移行対象になります。
中身まで全部Resourcesにあると、移行の範囲がそのぶん広がります。
入口だけに絞っておけば、移行するときに見るべきファイルが少なく済みます。
Resourcesの注意点は、以前の記事でも触れました。
ついでの落とし穴
移動作業の中で、2つ引っかかったことがありました。
アセットを大量に動かすときに起きやすいので、共有しておきます。
SaveAssetsが無関係な変更を連れてくる
移動後に AssetDatabase.SaveAssets() を呼んだところ、移動と無関係なMaterialが14件書き出されました。
エディタ上でdirtyになっていたMaterialです。
NovaShaderのキーワード補完で、いつの間にかdirtyになっていました。
SaveAssets は、dirtyなアセットをまとめて書き出します。
移動だけのつもりでも、別の変更がコミットに紛れ込むわけです。
そこで git diff -M --summary で、全件が100%リネームになっているかを確認しました。
混ざった変更は戻しています。
アセットを移動したあとは、リネーム以外の差分が無いかを必ず見るようにしています。
「移動で壊した?」は前後比較で切り分け
移動後、プレビュー動画が全件「古い」と判定されていました。
「移動で壊した?」と一瞬焦りますよね。
切り分けのために、移動前のHEADを一時的なworktreeに展開しました。
そこで同じテストを流したところ、移動前から全件「古い」判定でした。
原因は、参照元のC#がすでに削除されていたことです。
今回の変更が原因ではないと分かったので、ハッシュの更新は見送りました。
更新しても意味が無いからです。
変更の前後で同じテストを流すと、どちらが原因かを短時間で切り分けられます。
worktreeを使えば、作業中のブランチを崩さずに済むのも助かるところ。
まとめ
Resourcesフォルダに置くのは、Resources.Load でパス指定して読む「入口」だけで十分でした。
- Resources配下は、使っていなくても全部ビルドに入る
- GUIDで参照されているアセットは、Resources外でも依存としてビルドに入る
- 今回Resourcesに残したのは
BattleStage/BattleStageOutput/BattleSequenceCatalogの3つだけ - TimelineやVFXのPrefab / Material / Mesh、VolumeProfileはRawDataへ移動
- 移動は
AssetDatabase.MoveAssetでGUIDを保ったまま行う SaveAssets後はgit diff -M --summaryで無関係な差分が混ざっていないか確認する
Resourcesは便利なぶん、気付くと中身が増えていきます。
AIと一緒に開発していると、規約の書き方ひとつで置き場所がずれることもありました。
一度、Resources配下で Resources.Load されているアセットを数えてみてください。
入口だけに絞れないか、見直すきっかけになると思います。

筆者のXをフォローしよう
- Unity 6






