Assetsについて、一般的に、2つレベルの観点があります。1つはUnityのデフォルトのプロジェクトディレクトリAssetsからのAssetsで、もう1つはUnityのパッケージングシステムAssetBundlesかあらのAssetsです。次に、これら2つの側面から正規化して、UnityのAssetsが何であるかを理解します。
###1、Assetsディレクトリ
Unityでプロジェクトを作成するとき、プロジェクトの下にデフォルトで作成されるいくつかのディレクトリに注意を払いましたか? Unity 2018.3.8を使用して空のプロジェクトを作成して見てみましょう(このシリーズの記事のプロジェクトまたは例はUnity 2018.3.8に基づいて作成されており、以下も同じ)。ディレクトリは次のとおりです。
合計4つのディレクトリがあり、それぞれの役割は次のとおりです。
Assets
Unityプロジェクトの実際のアセットディレクトリ。プロジェクトで使用されるアセット、コード、配置、ライブラリなどのすべての元のアセットは、このフォルダーに配置されている場合にのみUnityによって認識および処理されます。
Library
Unityによって処理されたアセットを保存します。ほとんどのアセットがAssetsディレクトリにインポートされた後、Unityを介してUnity承認ファイルに変換する必要があります。変換されたファイルはこのディレクトリに保存されます。
Packages
これは2018年以降に追加された新しいディレクトリで、Unity分離のpackagesコンポーネントを管理するために使用されます。私は以前の記事でこれを説明したことがる:https://zhuanlan.zhihu.com/p/77058380
ProjectSettings
このディレクトリは、Unityのさまざまなプロジェクト設定を保存するために使用されます。
プロジェクトにスクリプトファイルがあってから、コードコンパイル用にobjディレクトリが追加されます。したがって、プロジェクトを遷移したり、他の人にコピーしたりする場合は、Assets、Packages、ProjectSettingsの3つのディレクトリをバックアップするだけで済みます。Libraryについては、Unityを開くとチェックされ、自動的に変換されます。もちろん、プロジェクトが非常に大きく、アセットが非常に多い場合は、遷移中にLibraryを一緒にコピーして転送すると、変換時間を大幅に節約できます。
さらに、実際のプロジェクト開発中に、異なるプラットフォーム(例えば、PCパッケージやAndroidパッケージなど)に対してパッケージ化する必要があるかもしれません。その場合、2つのプロジェクトをコピーし、1つはPCプラットフォームに設定し、もう1つはAndroidプラットフォームに設定します。その方は、必要に応じてプラットフォームを切り替えるよりも効率がはるかに高くなります。なぜならば、別のプラットフォームに切り替えるたびに、Unityがすべての内蔵アセットをもう一度処理する必要があるため、非常に時間がかかります。
##2.AssetBundles##
他のすべての理解はさておき、英語の命名の観点からすると、これは一種のバンドルであり、Assetsをアーカイブするための形式です。その概念は、ZipまたはRAR形式を使用してアセットを圧縮、暗号化、およびアーカイブや保存するというものです。区別しているところは、Zipなどの圧縮形式はファイル用であるのに対し、AssetBundlesはUnityのAssets用であるという点です。そして、視点を変えると、実際には、Zip操作はオペレーティングシステムが認識できるファイルをアーカイブし、AssetBundles操作はUnityが認識できるファイルをアーカイブします。この点だとすれば、両者の役割はほとんど同じです。
そのような導入を踏まえ、Unity公式の定義を見てみましょう。
AssetBundlesは、特別なプラットフォームと非コード形式のAssetsを含むアーカイブファイルです。
公式ドキュメントリンク:https://docs.unity3d.com/Manual/AssetBundlesIntro.html
ここにはいくつかの重要な情報があります。1つ目はアーカイブファイル(つまり、バンドルされたファイルタイプ)です。2つ目はプラットフォームの違いがあります。3つ目はコードが含まれていません。最後にUnityのAssetsを保存します。
これまでAssetsについて話してきましたが、Assetsとは一体何ですか?そして、Unityのエンジンでの位置付けはなんですか?
###3.Unity資産
Windowsオペレーティングシステムは、サフィックス名を使用してファイルを認識します。サフィックス名と対応する処理ソフトウェアをシステムに登録し、ファイルをダブルクリックすると、システムは指定されたソフトウェアを呼び出してファイルを解析および処理します。システムに登録されていない場合、またはサフィックスが削除されている場合、オペレーティングシステムはこのファイルを認識できません。
UnityのAssetsもそれと同じ、一つのAssetsを資産アセットと呼んでおり、Unityが認識できるファイルに理解できます。ここには実際には2つのタイプがあります。1つはUnityにネイティブにサポートできる形式(材質球など)で、もう1つはUnityで処理してからサポートできる形式(FBXなど)です。処理する必要があるフォーマットに対し、Unityはインポーター(Importer)を提供しております。次の図に示すように、コードにImporterと入力すると、多くの種類のインポーターがあることがわかります。
すべての元のAssetsファイルはUnityプロジェクトのAssetsディレクトリに配置し、Unityで処理した後にLibraryディレクトリに保存する必要があることに注意してください。
##二、Assetsの識別と参照
資産ファイルとして、Assetsは多くの種類があります。例:材質球、テクスチャスタンプ、オーディオファイル、FBXファイル、さまざまなアニメーション、配置、またはClipファイルなどがあります。通常、Unityでドラッグ、追加、変更、名前変更、ディレクトリ変更などの様々操作を行います。しかし、Unityエンジンでいかに操作しても(削除を除く)、関連する参照は失われないのはどうしてでしょう?
###1.AssetsとObjects
本文に入る前に、いくつかの概念を定義しましょう。(後のシリーズ文章もそれを参照)Assets:Unityの資産を指します。Unityの「Projects」ウィンドウに表示される単一のファイル(またはフォルダー)を意味しています。
Objects:UnityEngine.Objectから継承されたObjectsを指します。シリアル化可能なデータで、特定のアセットのインスタンスを記述します。Unityエンジンにサポートできる類型を表しています。例えば、Mesh、Sprite、AudioClip /AnimationClipなどがあります。
ほとんどのObjectsはUnity内蔵でサポートされていますが、2つの例外があります。
ScriptableObject
開発者向けのカスタムデータ形式を提供しています。このクラスから継承されたフォーマットは、Unityのネイティブタイプのようにシーケンスおよび逆シリアル化でき、UnityのInspectorウィンドウから操作できます。
MonoBehaviour
MonoScript向いている変換器を提供しています。 MonoScriptはUnityの内部データ型で、実行可能なコードではありませんが、特定の名前空間とプログラムの下に保存し、特定または特別なスクリプトへの参照を保持します。
AssetsとObjectsの間には1対多の関係があります。たとえば、一つのPrefabは一つのAssetsと見なし、中には多くのObjectsが含まれます。一つのUGUIにおけるPrefabの場合、Text、Button、Imageなど、多くのコンポーネントがあるかもしれません。
###2.File GUIDsとLocal IDs
UnityEngine.Objectsが相互に参照できることは、Unityに慣れた人にとって常識のもののようです。ただここには一つの問題が起こりました。相互参照されるObjectsは、同じAssetsにある場合と、異なるAssetsにある場合があります。たとえば、UGUIのImageはSprite AtlasにおけるSpriteを参照する必要があります。それで、Unityが堅牢なアセットIDを持ち、さまざまなアセットの参照関係を安定して処理できる必要があります。さらに、Unityは、これらのアセットIDがプラットフォームとは関係がないことも考慮する必要があります。クロスプラットフォームのエンジンだから、開発者に、プラットフォームを切り替える時にアセットの参照関係にも注意を払わせるのは筋道が通りません。
これらの特定の要件に基づいて、Unityはシリアル化を2つの部分に分割します。一つはFile GUIDと呼ばれ、資産の位置をマークします。このGUIDは、内部アルゴリズムに従ってUnityによって自動的に生成され、共通ディレクトリ、共通名前、拡張子は「.meta」であるファイルに保存されます。
ここで注意すべき点がいくつかあります。
Unityは、初めてAssetsをインポートするときにAssetsを自動的に生成します。
Unityパネルで位置を移動すると、Unityが自動的に.metaファイルを同期します。
Unityが開いている場合は、.metaを個別に削除します。Unityは、再生成されたGUIDが既存のGUIDと同じであることを確認できます。
Unityを閉じるときに、.metaファイルを移動または削除すると、Unityは元のGUIDを復元できません。これは、参照が失われることを意味します。
資産ファイルを決定したら、Local IDsで現在のObjectsが資産における唯一なマークを表している。File GUIDは資産が全体のunityプロジェクトに唯一であると、Local IDはObjectsが資産に唯一であると保証します。2つの組み合わせを通じて対応する参照をすばやく見つけることができます。
Unityは、資産GUIDとパスのマッピングテーブルも内部で維持します。新しいアセットがプロジェクトに入るか、一部のアセットが削除されるか、アセットパスが調整されるかのたびに、Unityエディターはこのマッピングテーブルを自動的に変更します。この方法でAssetsの場所を的確に記録します。したがって、.metaファイルが失われるか、別のGUIDに再生成されるかなどの状況で、Unityは参照を失います。プロジェクトの表示では、スクリプトに「Missing」に表示されるか、一部のマテリアルが失われると、シーンがピンク色になります。
###3.Libraryにおけるアセット
Unityでサポートされていないフォーマットは、インポーターによるアセットの変換が必要であることを前述しました。このセクションで再びアセットの位置について説明する理由は、File GUIDとの関わりです。インポートするたびにアセットを比較するのは時間のかかるだから、アセットの変換とストレージが必要である理由も、次回の起動にアセットを再度処理必要なくなるためです。簡単に言うと、すべての変換結果は、Library / metadata /ディレクトリに保存され、File GUIDの最初の2桁にちなんで名付けられたフォルダーに保存されます。例えば:
注:ネイティブでサポートされているAssetsにも同じ保存プロセスがありますが、インポーターを使用して変換する必要はありません。
###4.Instance ID
File GUID和Local IDは、確かにエディターモードでプラットフォームに依存せずに、アセットの場所と参照関係をすばやく特定できて維持し、Unityの計画を完成するのに役立ちますが。実行中にパフォーマンスの問題が発生します。言い換えれば、実行時に、よりパフォーマンスがいいシステムが必要となります。そこで、Unityは別のキャッシュセットを作成しました(以前のキャッシュセットを覚えていますか?GUIDとファイルのパス関係を記録するために使用されます)。 PersistentManagerは、File GUIDsとLocal IDsを簡単な、Session唯一な整数に変換するために使用されます。これらの整数はInstance IDです。Instance IDとは、逓増する整数です。新しいObjectsをキャッシュに登録する必要がある場合は、逓増するだけで十分です。PersistentManagerの詳しい分析については、《深度剖析PersistentManager.Remapper内存占用》(中国語注意)を参照してください。
簡単に言うと、PersistentManagerは、Instance IDとFile GUID、およびLocal ID間のマッピング関係を維持し、Objectソースデータの場所を特定し、メモリ内(存在する場合)にObjectのインスタンスを維持します。システムがInstance IDに解析される限り、このInstance IDを表すロードされたObjectsをすばやく見つけることができます。Objectがロードされていない場合、File GUIDとLocal IDは、指定されたAssetsをすばやく特定して、即時にアセットをロードします。写真付きの記事が2つ見つかったので、リンクから確認できます。
UWA Blog:《Unity文件、文件引用、Meta详解》(中国語注意)
TencentGAD:《程序丨入门必看:Unity资源加载及管理》(中国語注意)
また、UnityPrefabやmetaファイルはYAMLで保存されます。YAMLについては、以下を参照してください:https://baike.baidu.com/item/YAML/1067697?fr=aladdin(中国語注意)
##三、アセットのライフサイクル
これまで、UnityのAssetsがエディターと実行時の関連と参照関係をはっきりにしました。次に、これらのアセットのライフサイクルと、メモリ内でどのように管理されるかに注意を払う必要があります。これにより、ロード時間とメモリ使用量をより適切に管理できるようになります。
###1.Objectローディング
Unityアプリケーションが起動すると、PersistentManagerのキャッシュシステムは、プロジェクトがすぐに必要とするデータ(起動シーンのデータまたはその依存項目など)と、Resourcesディレクトリに含まれるすべてのObjectsを初期化します。実行時にAssetsをインポートするか、AssetBundleからObjectをロードする(たとえば、リモートでダウンロードしたもの)と、新しいInstance IDが生成されます。
さらに、次の条件を満たすと、Objectが自動的にロードされます。
ObjectのInstance IDは間接的に参照される
Objectは現在メモリにロードされていない
Objectのソース場所を特定できる(File GUID 和 Local ID)
さらに、File GUIDとLocal IDにInstance IDがないか、Instance IDがあるが、対応するObjectsがすでにアンインストールされており、このInstance IDが無効なFile GUIDとLocal IDを参照している場合、このObjectsの参照は保持されますが、実際のObjectsはロードされません。 Unityエディターでは、「(Missing)」参照としてに表示します。実行時のObjectsタイプによっては、nullポインターになったり、メッシュまたはテクスチャスタンプが失われたり、シーンまたはObjectsがピンクに見えます。
###2.Objectsのアンインストール
ロード以外、Objectsは特定の状況下でアンロードされます。
(1)未使用のAssetがクリア操作をすると、対応するObjectが自動的にアンインストールされます。通常、シーンを切り替えるか、手動でResources.UnloadUnusedAssetsのAPIを呼び出すときにトリガーされます。ただし、このプロセスでは、参照がないObjectsのみがアンインストールされます。
(2)ResourcesディレクトリからロードされたObjectsは、Resources.UnloadAssetAPIを呼び出して明示的にアンインストールできます。ただし、これらのObjectsのInstance IDは引き続き有効で、且つ有効なFile GUIDとLocalIDが相変わらず含まれます。Monoの変数または他のObjectsがResources.UnloadAssetによってアンロードされたObjectsへの参照を保持している場合、このObjectsは直接または間接的に参照された直後にロードされます。
(3)AssetBundlesから取得したObjectsは、AssetBundle.Unload(true)APIが実行された直後に自動的にアンロードされ、これらのObjectsのFile GUID、Local ID、およびInstance IDがすぐに無効になります。アクセスしようとすると、NullReferenceExceptionがトリガーされます。ただし、AssetBundle.Unload(false)APIが呼び出された場合、ライフサイクル内のObjectsはAssetBundleとともに破棄されませんが、UnityはFile GUID、Local ID、および対応するObjectsのInstance ID間の接続を中断します。つまり、これらのObjectsが将来のある時点で破棄された場合、これらのObjectsが再度参照されたときに、それらを自動的に再ロードする方法はありません。
さらに、ObjectsがソースAssetBundleとの接続を中断し、同じAssetを再度ロードした場合、Unityは以前にロードされたObjectsを再利用しません。代わりにInstance IDを再作成します。そうすると、メモリに複数の冗長アセットが増えるようになりました。
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析と最適化ソリューション及びコンサルティングサービスを提供している会社でございます。
今なら、UWA GOTローカルツールが15日間に無償試用できます!!
よければ、ぜひ!
UWA公式サイト:https://jp.uwa4d.com
UWA GOT OnlineレポートDemo:https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ:https://blog.jp.uwa4d.com