Uniteより。
講演者情報
高橋啓治郎氏
http://www.radiumsoftware.com/index.html
雑談
全てのソフトウェアはユーザがメンタルモデルを構築する事で、実装等を考えなくても使える。
これはひとえにUIやUXによるところが大きい。
とかそういう話。
シーンのオーバーヘッド
コンパイルするとどうなってるか?
まずは実演。簡単なシーン3つ(以下の通り)をコンパイルしてみる。
- 1とかかれたテクスチャがひっついたボックスと、2とかかれたテクスチャのボックスがならぶシーン
- 3と書かれたボックスと4とかかれたボックス
- 4とかかれたボックスと5とかかれたボックス
これをWebPlayer用にコンパイルすると1つのデータと1つのHTMLになる。
これだと中身がわからないので分かるデータにアクセスしてみる。
tempディレクトリ
ビルドをした後、Unityを閉じずにTempディレクトリにアクセスすると以下のようになっている。
※画像はシーンが2つのものでテストしたのでファイル数は少ない
シーンは3つだったのにコンパイル後のデータは6つ。
- mainData
- sharedassets0.asset
- sharedassets1.asset
- sharedassets2.asset
- level0
- PlayerConnectionConfigFile
この中にはUnityのシーンで使用するデータが羅列されている。
中身はバイナリなので読む事が出来ないが、sharedassetsはbinarytotextで読める様になる。
binarytotextについて
binarytotextはlinux系のコマンドで、human-readableな文字列に変換してくれる。
ざっとみたところいくつかのやり方があるようで、Uniteで実演されてたのはターミナルから変換するやりかただった。
コンパイル済みシーンデータ(sharedassets)の内容
定義されたデータは、シーン間のデータの重複が無い様に以前に参照するだけでデータ内容は定義されない。自分より若いシーン(buildセッティングで順番を定義)で定義済みだった場合はその後のシーンデータには参照のみで、値は定義されない。
binarytotextで開くとデータはid単位で区分けされ、その区分内に使用するプレファブやテクスチャ等のデータが入っている。
ID1で定義されたデータはプリロードデータ。2以降にシーン内のデータが定義されるが、既存の定義済みデータを参照する場合はID1に記述される。
シーンの読み込みと破棄
loadSceneはシーンを読み込んでから破棄する。
重複する可能性があるので読み込んでからでないと破棄していいか分からないから。
よって、2シーンがメモリ上に展開される事になり、かなりメモリを多く読む事になる。
フレーム間の処理はインスツルメンツを使う。
DevelopmentBuildでないと関数のシンボルが外れてInstrumentsでも良くわからない事になる。
シーンを読み込む時のメモリ状況についてはUnityのプロファイラでは負いきれない。
Unityのプロファイラは1フレーム単位でしか管理できないため、フレーム内の状況についてはXcodeを使った方が分かりやすく見える。
InstrumentsのCPUタイムプロファイラ
XcodeのツールからInstrumentsを開き、CPU Time Profilerを起動する。
画像のようにウィザードが出現するのでTimeProfilerを選択すればOK。
Instrumentsで関数単位で追う事ができる。何にCPUの何パーセントが使われているかとかが全部出ている。
コールツリーにすると処理の順番がツリー構造で見える。
プロファイルのビルドコンフィグでリリースになっているのをデバッグに変更しておく。これをしておかないとシンボルが切れてしまって何が起こってるのか終えなくなる。
https://gist.github.com/ogkslownin/5465729
一定時間でマテリアルのカラーを数十回、数百回変更し、さらに時間が経過すると自身を5体生成する。
実際にInstrumentsでは”MaterialChanger_ChangeColor_…”と表示されているので、ああ重いんだなぁと推測できる。
loadSceneAsync等の非同期読み込みがあるけど、前述のとおり、メモリに2シーン展開するのでメイン側でガク付くことが多い。これはメモリまわりのプロファイラでチェックする。記事では省略。
メモリの話
メモリの領域は大きく分けて3つの世界に分割できる。
- システム
- メモリマネージャ
- Mono
システムはOSトカ。メモリマネージャが入ってるところがUnityの領域。MonoはC#のランタイムとか入ってるところでGCが動くけどあまり直接的には手を出せない。
GC.Collect()
これで強制的にGCを発動できるけど動作が保守的で確信を持って参照が無い事が約束されない限りは回収されない。よってここにあまり期待しないほうがいい。
Unityの領域はロードレベル。一度読んだら追加で読まないのが基本。
ユーザレベルではそういうわけにはいかない。
シーン内でメッシュやテクスチャを生成したりするので増減は発生する。
メッシュをnewしたものは自分でdestroyすること。
loadしたものはunloadすること。
こういうのはunity管理対象外。
unloadは4.1以降軽くなった。
Assetbundle
データの構造はWebPlayerでコンパイルしたみたいな構造。
AssetBundleのヘッダと実データ構造体の連番で管理されている。
プリロードテーブル、コンテナマップ(読むべきものを定義)、メインアセット(プロパティ参照用)
ハッシュが一致しないとアセットバンドルをダウンロードできない。
データが他と重複するなら依存性を構築して外部参照させることもできる。
ただし、これをすると参照先のアセットバンドルのデータの並びが変更されたり、先に説明した連続体のうちの若い番号がなくなることによってナンバリングが変わる等が怒らない様にしなければならない。
この対処法として32bitのIDで検索する事が出来る様になる方法もある。
Deterministic option で連番からID管理で検索する方式へスイッチ。
これをやると並びはぐちゃぐちゃになるのでシークに時間がかかるようになる。
iOS device等であればランダムアクセスなのでその限りではない。
DVDロムでゲームを配信する等の場合に限り期を付けるべきこと。
素敵な内容で、大好きです