[Unity] 60fps 60フレームレートでの瞬間的なフリーズと incremental GC

03-30,2024

60fps固定のゲームなんか作ることは永久にないだろうと思っていたのに
必要なシチュエーションが生まれてしまい、
それをきっかけにして、今回の問題にぶちあたりました。


・問題


通常は問題なく動作しますが、
1分に1回くらい、瞬間的にフリーズし、またすぐに動き出すという現象が発生しました。
止まっている時間はわずか0.1秒くらい。
しかし、60fpsだと、それが無茶苦茶目立ちます。


60fpsは、厳密には、その刹那の隙間を人間の目は認識できないレベルですが、
俯瞰した時の全体像の雰囲気は、滑らかさとして人間の目にもはっきりと分かります。

また高速で移動するオブジェクトの場合は、fpsが低いと、
映像の中のヘリコプターの羽と同じように、二重や三重になって目に見えます。
しかしfpsが高ければ、そのダブりが、滑らかな連続体のように認識できるというメリットがあります。

が、

Unityは、ちょっとしたことですぐにメモリゴミが発生しやすく、
また、そのメモリゴミをヒステリックに解消しようとする思想があるのか
小規模のプログラムでも、
ゴミを解消しようとする内部プログラムが動き出します。

今回の瞬間的なフリーズは、これが原因だろうと推測しました。


現在のPC環境では、ほとんど目立たないかもしれませんが、
モバイル環境だと、今のハイスペックデバイスでも、このメモリゴミ処理がそこそこ大きくなります。

ただ、通常のモバイルデバイスは、デフォルトで30fpsに固定され、
30fpsだと、そもそも滑らかさが低いので、通常のゲームなら気にする機会はほぼないでしょう。

しかし、60fpsを超えると、滑らかさが発生します。
この滑らかさゆえに、一瞬のゴミ処理が、際立つことになります。
また、30fpsの倍のスピードでゴミも発生することになるので、
その瞬間的なカクつき頻度も倍になります。


もちろん、
unityの仕様にのっとり、極力ガベージアロケードが発生しないような取り組みが前提ではありますが
それでも、unity内部のせいや、他のライブラリなどが原因でメモリゴミが発生することを回避するのはかなり困難だと思っています。

そして、数百バイト程度しかないゴミを、
Unityは、わざわざStop the worldして、処理してくれているようです。
60fps以上だと、その一瞬に、わずかに画面がカクッと止まるのが、
めちゃくちゃ目立ちます。


・対策


そこでUnity2019頃から誕生したのが、
 incremental GC
という機能のようです。


Player Settings 内の Configration内にその項目はあります。
各プラットフォームごとにチェックボタンが存在しており、
デフォルトはオフのようですが、バージョンやテンプレートによっては
最初からオンであることもあるようです。


その機能の内容は、ググると沢山でてきますが、
つまり、通常は一定ごとにまとめて行っているカベージ処理を、
まとめてではなく、常時少しずつ行うようにする、というもの。

ゴミ処理の分散化 

という意味のようです。


分散することで、全体の負荷はわずかに上昇してしまうようですが、
瞬間的なstop the worldへの抑制を期待できそうです。


というわけで、結局、
プロファイラーを血眼で睨みつづけて格闘しましたが、
どれだけやっても60fps下でのカベージアロケードによる瞬間的フリーズを
やっつけることができなかったので、

この incremental GC に頼ることになりました。
これは私の敗北です。

しかし、これをオンにすると、
stop the worldが消えたので、良しとすることにしました。



[Unity]動的メッシュ生成時のマテリアルが反映されない問題 & 動的生成メッシュへのテクスチャ方向問題

03-21,2024

今日は、
Unityで、コードから、動的にメッシュを生成し、ポリゴンを生成した際における
2つの問題についてぶちあたったので、書き留めておきます。


1つ目
◆動的メッシュ生成時のマテリアルが反映されない問題


mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();

などによって、
独自にポリゴンメッシュを生成した場合、

そのオブジェクトに、
MeshRendererを、AddCompornentして、Materialを引き渡しても、
ちっとも反映されません。原因は不明です。
Inspectorで確認すると、マテリアルはちゃんと引き渡されていますが、
シーン上でも画面上でも透明になっていて、最初はメッシュ生成に失敗しているのかと
勘違いしてしまいます。
Inspector上でマテリアルを再設定してやると何故か反映されます。謎。


◇解決方法
オブジェクト自体は最初から用意しておき、
そのオブジェクトに、最初から空のMesh Filterを追加しておきます。
更に、マテリアルをセット済みのMesh Rendererを追加しておきます。

コードでは、その空のMesh Filterのインスタンスに対して、
動的にメッシュを生成し、
マテリアルは、既に追加しているマテリアルコンポーネントをGetConpornentして取得し、
更新するやり方でうまくいきます。



2つ目
◆動的生成メッシュへのテクスチャ方向問題


例えば、Y軸の高さが0の、平面ポリゴンメッシュを生成した場合、
そこにマテリアルでテクスチャやシェーダーを貼ると、UVの方向がうまく行かない場合があります。
おそらく、デフォルトのUV方向がZ軸方向になってたりして、生成した平面に対して間違っているせいかと思われます。
(動的生成メッシュ関係なく、UV方向によっては、普通のQuadポリゴンでもなるかも)

Mesh.setUVで、UV座標をいじっても解決できそうですが、
Shader Graphで、positionノードから特定軸だけとかをsplitして、それをテクスチャ等に繋ぐことで、UV方向を変えてやるのが簡単そうです。