この記事は【unityプロ技②】 Advent Calendar 2019の14日目の記事です。
記事に登場するコードはパブリックドメインです
#NestedPrefabは爆速である?
Unity2018.3より前から開発していたプロジェクトにおけるフィールドデータで、現在のNestedPrefabのようにプレハブを入れ子にした場合でも子プレハブ側の変更が反映されるようにしたいことがありました。
これを実現するため、プレハブ内には仮オブジェクトを置いておき、実行時にプレハブから生成したものに差し替えるようにしていました。
これをNestedPrefabに移行した際に強く感じたのが シーンのロードが爆速になった!! ということです。
NestedPrefab is 爆速? 今回はこの仮説をエディタ上で簡単に検証してみました。
#確認環境
- macOS High Sierra 10.13
- プロセッサ 2.6 GHz Intel Core i7 クアッド
- メモリ 8 GB 1600 MHz DDR3
- Unity2019.2.13f1
#Instantiate vs Nested
まず実行時に生成するパターンですが、以下のようにSerializeFieldに設定したプレハブからInstantiateします。
[SerializeField] private GameObject[] prefabs = null;
void Awake()
{
var t = transform;
for (int i = 0; i < prefabs.Length; ++i)
{
Instantiate(prefabs[i], t);
}
}
![スクリーンショット 2019-12-14 13.59.51.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F530201%2Fcdbe20d0-8d53-fd40-a5ee-b8daeca274ee.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=5b20a168900f688bece7e7440ec7a623)
Nestedのほうは直接プレハブの中に入れます。Instantiateのほうでは子プレハブをInstantiateするためのスクリプトを付けていることやAwake()
がコールされることのオーバヘッドで速度差が出るかもしれないので、空のAwakeだけ実装したスクリプトをルートに貼り付けておきました。
![スクリーンショット 2019-12-14 14.11.20.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F530201%2Fd9260627-fd9e-44f1-9f73-8393c8cfa5ad.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=b5a860f7d24348cd0ca0b0a57a6307ba)
![スクリーンショット 2019-12-14 14.14.45.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F530201%2Fb1029c06-dfc0-a668-a6df-fefbd472ce7c.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=a38ad417e3bae51fcac291584ab5e721)
どちらもプレハブのロードにはResources.Load()
を使用します。また、生成が1個だけだとすぐ終わってしまうので同じプレハブを100個生成します。この条件での検証結果は以下の通りです。
Serialize & Instantiate | Nested | |
---|---|---|
Resources.Load() | 2ms | 2ms |
100個生成 | 13〜14ms | 7〜8ms |
ロード時間に差はなしでしたが、生成は40%ほど早くなっていました。
Instantiateを使用することによるオーバヘッドを考えれば想像がつくことではありましたが、やはり実測値としてもNestedPrefabを利用することで高速化が見込めることは間違いなさそうです。
ここでは子プレハブを参照するのにSerializeFieldを使用していますが、子プレハブもそれぞれResources.Load()
でロードしてくるようなケースではロード時間にも大きな差が出るでしょう。AssetBundleなら尚更です。
#Nested vs Unpack
ついでに子プレハブを全てUnpackして1つのプレハブに統合した場合との比較も行いました。
![スクリーンショット 2019-12-14 14.32.05.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F530201%2F27766a60-8382-8238-d701-b1d3eb7091a4.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=f8319f4e68192cddad8e96ebfdc09b06)
Nested | Unpack | |
---|---|---|
Resources.Load() | 2ms | 2ms |
100個生成 | 7〜8ms | 7〜8ms |
こちらは特に差は出ませんでした。どちらの場合でも内部で最大限に最適化されていることが予想できます。
#まとめ
今回の検証ではNestedしているプレハブは5個だけでしたが、僕が爆速になったと体感したフィールドデータはもっと大量のオブジェクトが入っていたので規模が大きくなればなるほどNestedPrefabの恩恵も大きくなるでしょう。
NestedPrefabを含めプレハブのワークフローが改善されたことで利便性がとても向上しましたが、同時にパフォーマンス最適化の面でも活用の幅が広がったように思います。