- 投稿日:2020-09-18T20:47:54+09:00
パフォーマンス最適化、歩きは止まらないーーメモリ編
プロジェクトのパフォーマンスの最適化は、主にCPU、GPU、メモリの3つの方面を中心に行われます。ゲームであれ、VRアプリケーションであれ、メモリ管理は開発段階の最優先事項です。
ただし、私たちが評価した大量のプロジェクトでは、90%以上でさまざまな程度のメモリ使用問題が発生しています。
現在、Unityエンジンに基づくモバイルゲームとモバイルVRゲームに対して、メモリコストは次の3つの部分に集めています:
1.アセットメモリ使用率;
2.エンジンモジュール自身のメモリ使用率;
3.マネージヒープメモリ使用率。
プロジェクトにメモリの問題があれば、上記の3つの状況に避けられません。今日は、これらの3つの状況を1つずつ説明します。
アセットメモリ使用率
比較的複雑な大規模および中規模のプロジェクトでは、アセットのメモリ使用量が総メモリの70%以上を占めることがよくあります。だから、アセットの使用が適切かどうかは、プロジェクトのメモリ使用量を直接決定します。一般的に、ゲームプロジェクトのアセットは、テクスチャ(Texture)、メッシュ(Mesh)、アニメーションクリップ(AnimationClip)、オーディオクリップ(AudioClip)、マテリアル(Material)、シェーダー(Shader)、フォント(Font)およびテキストアセット(Text Asset)などのタイプに分類できます。その中、テクスチャ、メッシュ、アニメーションクリップ、オーディオクリップ四つのアセットは、大きなメモリコストを引き起こす可能性が最も高いアセットです。
一、テクスチャ
テクスチャアセットは、ほとんど全てのゲームプロジェクトで最大のメモリコストを占めるアセットであります。60,000パッチのシーンの場合、最大メッシュアセットは10MBのみですが、2048x2048テクスチャは直接16MBに達する場合があります。だから、プロジェクトでテクスチャアセットを適切に使うかどうかは、プロジェクトのメモリ使用量に大きく影響します。
では、テクスチャアセットを使用する場合、何に注意すべきですか?
(1)テクスチャ形式
テクスチャ形式は、開発チームにとって最も重要なテクスチャ属性です。これは、テクスチャのメモリ使用量だけに影響するではなく、テクスチャのロード効率も決定するためです。一般的に、開発チームにハードウェアのタイプに応じて、サポートできるテクスチャ形式をできるだけ選択することをお勧めします。例えば、AndroidプラットフォームのETC、iOSプラットフォームのPVRTC、Windows PCのDXTなど。そのため、UWAレポートでは、開発チームが迅速に問題を特定できるように、テクスチャ形式を詳細にリストしています。
ハードウェアでサポートされているテクスチャフォーマットを使用すると、次の問題が発生する場合があります。カラースケール問題
ETCやPVRTCなどの形式はすべて非可逆圧縮であるため、テクスチャの色差の範囲が大きい場合、必然的にさまざまな程度の「階段」のようなカラースケールの問題が発生します。したがって、多くの開発チームはより良い結果を達成するためにRGBA32 / ARGB32形式を使用しています。ただし、この方法は大きいメモリ使用率を導きます。例えば、同じ一枚の1024x1024テクスチャそしてMipmapをオンにしない場合、PVRTC形式のメモリ使用率は512KBになり、RGBA32ビットに変換すると、おそらく4MBに上昇します。だから、開発チームがRGBA32またはARGB32形式のテクスチャを使用する場合は慎重に検討する必要があります。より賢い選択は、テクスチャの色差の範囲を最小限に抑え、ハードウェアがサポートする圧縮形式をできるだけ多く使用して保存することです。
ETC1は透過チャンネルをサポートしていない問題
Androidプラットフォームでは、OpenGL ES 2.0を使用するデバイスに対して、テクスチャ形式はETC1形式のみをサポートできます。この形式にはより深刻な問題があります。それは、Alpha透明度チャネルをサポートしないため、透明テクスチャをETC1形式で直接保存できません。この点に関して、開発チームに透明テクスチャをできる限り2枚に分割することをお勧めします。つまり、一枚のRGB24ビットテクスチャは元のテクスチャのカラー部分を記録し、もう一枚のAlpha8テクスチャは元のテクスチャの透過チャンネル部分を記録します。そして、これらの2つのテクスチャを次々にETC1形式のテクスチャに変換し、特定のシェーダーでレンダリングして、透禍テクスチャをサポートする効果を実現します。この方法では、RGBA透禍テクスチャのレンダリング効果を大幅に近似できるだけでなく、テクスチャのメモリ使用量を減らすこともできます。
もちろん、現在OpenGL ES 3.0をサポートしているデバイスが増えているため、AndroidプラットフォームでETC2またはASTCをさらに使用できます。これらのテクスチャ形式は、透禍チャネルをサポートし、より理想的な圧縮率を持つテクスチャ形式であります。ゲームがミドルおよびハイエンドデバイスのユーザーに適している場合は、これらの2つの形式をテクスチャの主な保存形式として直接使用することもできます。
(2)テクスチャサイズ
一般的に、テクスチャサイズが大きいほど、メモリ使用量も多くなります。だから、テクスチャサイズをできるだけ小さくし、512x512テクスチャで表示効果が十分である場合は、1024x1024テクスチャを使用しないでください。後者のメモリ使用量は前者の4倍であるためです。そのため、UWAレポートでは、開発チームがすばやく検出できるように、テクスチャのサイズを詳細に表示します。
(3)Mipmap機能
Mipmapは、レンダリング帯域幅の圧力を効果的に軽減し、ゲームのレンダリング効率を向上させることを目的としていますが、Mipmapをオンにすれば、テクスチャメモリが元の1.33倍になります。より深度感のある3Dゲームに対して、通常は3DシーンモデルとキャラクターのMipmap機能をオンにすることをお勧めしますが、UWAがテストした沢山のプロジェクトでは、一部のUIテクスチャもMipmapをオンにすることがよくあります。実際にはこれは必要がないです。大多数のUIはスクリーンの最上層にレンダリングされます。Mipmapを有効にしてもレンダリング効率は向上できませんが、不要なメモリ使用量が増加します。したがって、開発チームにUWAレポートにMipmapで並べ、Mipmap機能をオンにさせるアセットはUIアセットですかどうかを確認することをお勧めします。
(4)Read&Write
一般的に、Unityエンジンで、テクスチャアセットの「Read&Write」機能がデフォルトでオフになっていますが、プロジェクトのディープな最適化に、多くのプロジェクトのテクスチャアセットがこのオプションをオンにすることに気づいてしました。これに対して、オンにするとテクスチャメモリが2倍になるため、開発チームにテクスチャアセットこのオプションの使用を厳密に注意することを提案します。
二、メッシュ
メッシュアセットは、複雑なゲームでより多くのメモリを使用する場合があります。メッシュアセットについて、使用するときに注意すべき点は何ですか?
(1)Normal、ColorやTangent
私たちが深く最適化した多数のプロジェクトでは、メッシュアセットのデータに、大量のColorデータ、Normalデータ、Tangentデータが含まれていることがよくあります。これらのデータの存在は、メッシュアセットのファイルサイズとメモリ使用量が大幅に増加させます。このうち、ColorデータとNormalデータは、主に3DMaxやMayaなどのモデリングソフトウェアをエクスポートするときに生成されますが、Tangentは一般的にエンジンにインポートするときに生成されます。
さらに面倒くさいのは、プロジェクトがメッシュにDraw Call Batching操作を行いと、全体的なメモリ使用量がさらに増える可能性があることです。たとえば、100個のメッシュが合併する場合、そのうち99個にはColor、Tangent、や他の属性がなく、残り1つにはColor、Normal、およびTangent属性が含まれます。このため、UWAレポートで各メッシュのNormal、Color、およびTangent属性の具体的な使用状況を示しています。開発チームは、各属性によって並べ替えて表示でき、冗長データを持つアセットを直接特定することができます。
一般的に、これらのデータは主にShaderがよりカッコいい効果を生成するために使用されます。だから、開発チームにプロジェクトのメッシュアセットを詳細なチェックをし、モデルのレンダリングシェーダーにこれらのデータをレンダリングする必要はあるかどうかを確認しますことをお勧めします。記事の長さのせいで、本日はテクスチャアセットとメッシュアセットのみをご紹介しますが、アニメーションクリップやオーディオクリップなどの他のアセットについては、UWAパフォーマンスレポートで直接確認することをお勧めします。同時に、次のアセットトピックで詳しく説明するので、しばらくお待ちください。
エンジンモジュール自身のメモリ使用率
エンジン自体のメモリコストは複雑で、大量の「小さな」メモリによって蓄積されていると認められます。例えば、GameObjectまたは他の各種Component(最大量なComponentはTransformはずです)、ParticleSystem、MonoScriptおよびさまざまなManager(SceneManager、CanvasManager、PersistentManagerなど)。
通常の状況では、上記のエンジンの各コンポーネントのメモリコストは比較的小さく、実際にメモリコストが大きいのは、WebStreamとSerializedFileの2つです。そのメモリ割り当てのほとんどは、AssetBundleのアセットローディングが原因です。簡単に言えば、new WWWまたはCreateFromMemoryでAssetBundleをロードする時に、Unityエンジンは元のデータをメモリにロードして解凍しますが、WebStreamのサイズは元のAssetBundleファイルサイズ+解凍されたデータのサイズ+ DecompressionBuffer(0.5MB)。同時に、Unityバージョン5.3以前のAssetBundleファイルはLZMAで圧縮されています。その圧縮率はZipに似ている(20%-25%)ため、1MBの元のAssetBundleファイルの場合、ロード後のWebStreamのサイズは5〜6MBになる場合があります。そのため、プロジェクトがnew WWWで複数のAssetBundleファイルをロードし、またAssetBundleをすぐに解放できない場合、WebStreamのメモリが非常に大きくなる可能性があります。これは、開発チームが注意すべき点であります。
SerializedFileの場合は、LoadFromCacheOrDownload、CreateFromFile、またはnew WWWローカルAssetBundleファイルを使用するときに生成されるシリアル化ファイルです。WebStreamおよびSerializedFileの場合、次の2つの点に注意する必要があります。
1)AssetBundleが完全にクリーンアップされていない状況があるかどうか。 開発チームは、Unityプロファイラーで具体的な使用状況を直接チェックし、Take Sample時のAssetBundleの存在が合理的かどうかを判断できます。
2)大きなWebStreamを占めるAssetBundleファイル(例えばUI Atlas関連のAssetBundleファイルなど)に対しで、LoadFromCacheOrDownLoadまたはCreateFromFileを使用して置き換える、つまり、解凍されたAssetBundleデータをローカルCacheに格納して使用することをお勧めします。このアプローチ、つまりメモリスペースをローカルディスクスペースに変えることは、メモリが特に限られているプロジェクトに非常に適しています。
マネージヒープメモリ使用量
Unityエンジンに基づくほとんどの現在のプロジェクトでは、マネージヒープメモリはMonoによって割り当てられ、管理されます。「マネージ」の本来の目的は、Monoが必要なメモリに適応するようにヒープのサイズを自動的に変更し、不要なメモリを解放するためにガベージコレクション(Garbage Collection)操作を行いことです。それてコードメモリ管理における開発者への技術要求を削減します。
ただし、これは、開発チームがコードに好きにマネージヒープメモリを添加することができるという意味ではありません。これは、現在Unityで使用されているMonoバージョンには重大な問題があるためです。Monoヒープメモリが割り当てられると、システムに返されません。 つまり、Monoのヒープメモリが増加するだけで減少しません。例を挙げます。プロジェクトが実行する時、シーンAに60MBのマネージヒープメモリを開きました、次のシーンBでは20 MBのマネージヒープメモリのみが必要な場合、Monoには40 MBの空きヒープメモリがあり、 システムには戻されません。これは私たちに対して非常に見えたくない状況です。ゲーム(特にモバイルゲーム)に対してメモリ使用率はとても高価のものであり、Monoに大量の不必要なメモリをロックさせるのは非常に無駄なことです。したがって、UWAレポートでは、開発チームのテスト中に累積された関数ヒープメモリ割り当てを統計しました。ヒープメモリ割り当てTop10の関数をチェックしたら、迅速に基礎コードの実現をチェックでき、不要なヒープメモリを割り当てるコードがあるかどうかを特定できます。
ここを読んで、あなたはそのような質問をするかもしれません:どの関数が大きなヒープメモリ割り当てを持っているか知っていますが、どうすれば不要なヒープメモリをさらに見つけることができますか?これは私たちがよくあった問題です。そのため、プロジェクトディープ最適化サービスに、プロジェクトチームに直接入って、現場でプロジェクトコードをチェックして問題コードを特定します。たくさんのディープテストをした後、ユーザーの不要なヒープメモリの割り当ては、主に下記の方面から起こします。
1)高频率のNew Class/Container/Arrayなど。開発チームは、Update、FixUpdate、またはコール頻度の高い関数でヒープメモリを開かないように注意する必要があります。これにより、プロジェクトのメモリとパフォーマンスが大幅に低下します。簡単に計算します。プロジェクトのある関数がフレームごとに100Bのヒープメモリを割り当て、フレームレートが1秒あたり30フレームであると仮定します。そして1秒のゲームのヒープメモリ割り当ては3KB、1分のヒープメモリ割り当ては180KBで、10分後に1.8MBが割り当てられています。そのような関数が10個あれば、10分後にヒープメモリの割り当ては18MBになります。この期間中に、Monoのヒープメモリのピーク値の上昇やGCの複数コールを導く可能性が高いです。私たちがテストしたプロジェクトでは、関数が10分以内に数百MBを割り当てるのがたくさんあって、時々にGB当たりのメモリ割り当て状況もあります。
2)Log出力。多くのプロジェクトで、まだたくさんのLog出力があることがわかりました。開発チームに自身のログの出力を厳密に制御し、不要なヒープメモリ割り当てを回避するためにキーログのみを保持することをお勧めします。これに関して、UWAレポートにLogの出力を詳しく検査します。詳細なパフォーマンスコストだけではなく、同時にLog出力のコールパスも表示します。そうすれば開発チームはレポートで直接にLogの出力を特定や制御できます。
3)UIPanel.LateUpdate。これは、NGUIでCPUとヒープメモリのコストが最も高い関数です。自身はただ一つの関数でありますが、NGUIの大量の使用がそれを無視できないルールに徐々になされています。この関数のヒープメモリ割り当てと自身のCPUコストは、根本としては同じで、UIメッシュの再構築であります。
コードヒープメモリの割り当てについて注意すべき点はまだたくさんあります。例えば、Stringリンク、一部のエンジンAPI(GetComponent)の使用など、これらは常に検討しまして、ここには記事の長さの原因で紹介しません。興味があれば、Googleで検索できます。以後にはコード効率に関する特別トピックもありますので、しばらくお待ちください。
UWAテストのメモリ標準
みなさんがUWAを使用した後、UWAが推奨するメモリ標準値について多くの疑問があります。 ここでは、UWAメモリ標準を作成するためのルールも紹介します。
(1)150MBの全体的なメモリ標準は、主に次の2つの要因から導き出されます。
①たくさんのプロジェクト最適化の後にまとめて得ました。実際、現在市場である主流のUnityゲームでは、メモリ使用量は主に120〜200MBに集中しています。同時に、iPhone4や512MB / 768MBなどのローエンドのAndroidモデルを考慮に入れると、そのアプリケーションの総メモリ使用量は200MBを超えることはできません(iPhone4の安全線は約180MBになるはずです)。ですから、UWAはReserved Totalを150MBに設定しました、これはUnityエンジン自身のメモリの割り当てです。目的はアプリがシステムライブラリを使用した後、OSの総なメモリも200MB未満になるためです。
②一部のチャネルは、AndroidゲームのPSSメモリに厳しい制限を課しています。通常、ゲームのPSSメモリは200MB未満である必要があります。これは、UWAがReserved Totalメモリを150MBに設定するもう1つの重要な理由です。
(2)総メモリが150MBに設定されると、さらに具体的な割り当てに設定をしました。ただし、説明したいのは、ここでのメモリ割り当ては実際には厳密な式を示せるものではなく、ただたくさんのプロジェクト最適化作業で私たちが得た経験であります。現在、プロジェクトの適切なメモリ割り当ては次のとおりです。
●テクスチャアセット:50 MB
●グリッドアセット:20 MB
●アニメーションクリップ:15 MB
●オーディオクリップ:15 MB
●Monoヒープメモリ:40 MB
●その他:10 MBより複雑なフォントファイル(Microsoft Yaheiなど)とテキストアセットは150MBで含まれていないことに注意してください。これらはゲームの要求次第で決定する必要があります。
(3)現在のUWAメモリ標準要求は少し厳しく、ミッドからハイエンドのデバイスに対して、許容量は実際には150MBをはるかに超えています。しかし、厳密な基準は、開発プロセスにおけるプロジェクトにとって良いものであると私たちは主張しています。少なくとも、それは誰にでも思い出させることができ、誰もが常に自分の問題に注意を払うことができます。私たちの了解により、現在のローエンド携帯電話のカバレッジ率は依然として非常に高いです。同時に、ミッドからハイエンドのモバイルデバイスに対しても、UWAもう実験と研究を行っています。遠くない将来に、さまざまなレベルのさまざまな設備に対してより合理的な推奨値を提供できるようになり、誰もがメモリをより簡単に管理できるようになれることを心から期待しています。
上記はゲームプロジェクトにある主なメモリ割り当て状況であります。ここを読んだみなさんに、Unityプロジェクトのメモリコストと潜在的な問題をよりよく理解し、自分のプロジェクトに対してより適切な最適化方法を見つけることを祈っています。
それ以外、開発チームに注意すべき点はまだ2点あり、これはメモリリークとアセットの冗長性です。次のメモリ最適化記事に関する内容を紹介します。
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析と最適化ソリューション及びコンサルティングサービスを提供している会社でございます。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
- 投稿日:2020-09-18T20:33:50+09:00
Unityエンジンモジュール分析
UWA GOTはUWAが提供しているローカルパーフォマンス最適化ツールです。Overview分析モードは開発者が直観的にソースコード内の性能ボトルネックを特定することができます。しかし、エンジンモジュールのパフォーマンスも非常に重要であり、ロジックコードの検出のみが、体系的かつ完全にボトルネックを特定するという目的を達成できないことがわかっています。そのため、本ツールでUnityエンジンモジュールの診断機能を提供しました。
詳細なエンジンモジュールパラメータ
現在、UWA GOT Onlineのレポートは、レンダリング、UI、パーティクル、アニメーション、および物理システムという6つの主流な重要なパラメータの測定モジュールをサポートしています。下図のように、レンダリングモジュールにおいて不透明度、半透明度、ビューのトリミングCPUおよびカメラの後処理などのCPUの消費時間、および特定のフレームの傾向および消費時間を確認できます。
また、テスト領域をスケーリングし、スクリーンショットを使用して、特定のピークでのパフォーマンスをより直感的に特定することもできます。
重要なパラメータについて、UWAの推奨値と関連な最適化意見を提供し、開発チームが問題の排除と改善をより的確に行うことができます。
正確なロジックコードスタック
スタック情報は、私たちが設定した性能のボトルの目であり、このバージョンの更新は、追加モジュールの重要なパラメータの検出に影響を与えず、代わりに、より詳細な追加関数のスタック情報を見ることができます。特定の調整用の状況を定義します。
特に、スタック情報の一覧表において、過去のデータとの比較可能な機能を提供します。これは、特定の重要なパラメータの変更傾向を確認し、開発チームも即時テスト結果検証することができます。
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析と最適化ソリューション及びコンサルティングサービスを提供している会社でございます。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
- 投稿日:2020-09-18T18:47:37+09:00
SQLiteUnityKit拡張ライブラリ
SQLiteUnityKit を下敷きにしたライブラリ
- 数多あるSQLiteUnityKitの改修のひとつです。
- リポジトリ (GitHub)
特徴
- 日本語が使えるようにしました。
- トランザクション処理に対応しました。
- バインド変数に対応しました。
- 既存DBを上書きしないようにしました。(かといって、マージもしません。)
- オープンやクローズ、リソースの開放などといった低レベル処理は隠蔽して、抽象化レベルの高い処理だけを表に出すようにしました。
- できるだけ例外は内部で捉えて、リソースの未解放を避けて動き続けるように務めました。
前提
環境
- Unity 2019.4.10f1 (LTS)
- Unity 2018.4.26f1 (LTS)
- Unity 2017.4 (LTS)
- SQlite 3.33.0
- C# 6
- 文字列補完(string interpolation)を使用しています。
- 必要なら、
PlayerSettings
でScripting Runtime Version
を4.x
に設定してください。SQLite
- SQLiteは、SQLのサブセットが使えるスタンドアローンなデータベース管理システムです。
- Windows、MacOS、Android、iOSなどに対応しています。
- 公式サイト
SQLiteUnityKit
- SQLiteUnityKitは、UnityからSQLiteを使用するためのフレームワークです。
- リポジトリ (GitHub)
導入と概要
- リポジトリから
Assets
をプロジェクトへ導入してください。概要
Assets/Plugins/sqlite3/
- 各プラットフォーム向けのSQLiteプラグインです。(iOSはOS側でサポートがあります。)
Assets/Scripts/
SQLiteUnity.cs
- 必須部分です。
- "SQLiteUnityUtility.cs"
- 拡張ユーティリティクラスです。お好みでどうぞ。
- トランザクションでも擬似的なバインドが使えるようになっています。
- "Test.cs"
- デモ用スクリプトです。
Assets/Prefabs/Console.prefab
- デモ用プレハブです。
Assets/Scenes/SQLite_Test.unity
- デモ用シーンです。
最新プラグインへの更新
- 公式サイトのダウンロードから最新版を取ってきて
Assets/Plugins
へ導入してください。- Androidについては、こちらの記事(Qiita)を参考にしてください。
基本的な使い方
- データベース
public class SQLite : IDisposable
- 新規生成 (初期化クエリ) (既にあれば単に使う、元があればコピーして使う)
public SQLite (string dbName, string query = null)
- 単文を実行
public void ExecuteNonQuery (string query, SQLiteRow param = null)
- 単文を実行して結果を返す
public SQLiteTable ExecuteQuery (string query, SQLiteRow param = null)
- 単文の変数を差し替えながら順に実行
public void ExecuteNonQuery (string query, SQLiteTable param)
- 同じSQL文を、パラメータを変えながら繰り返し実行します。
- 複文を一括実行し、誤りがあれば巻き戻す
public bool TransactionQueries<T> (T query) where T : IEnumerable<string>
public bool TransactionQueries (string query)
- 複数行を配列やリストで渡すか、単一文字列として渡すか、という違いです。
- 冒頭と末尾に
BEGIN
,COMMIT
が勝手に付きます。- 行列データ
public class SQLiteTable
- クエリで返されるデータで、列の定義と行データの集合です。
- 行データ / バインドパラメータ
public class SQLiteRow : Dictionary<string, object>
- 1行分のデータで、列データの集合です。バインドパラメータを渡すときにも使います。
- 拡張バインド (トランザクション用)
public static string SQLiteBind (this string query, SQLiteRow param)
- sqliteの外側で行われる文字列ベースのバインドです。
その他
- ご指摘やご提案、あるいはご質問などを歓迎します。
- 常識的なことも理解していないので、何か間違えているような場合はご助言いただけると助かります。
- より詳しい使い方については、使用例を記事にしたいと考えています。
- 投稿日:2020-09-18T07:30:54+09:00
「【Unite 2017 Tokyo】最適化をする前に覚えておきたい技術」の動画を見たメモ
概要
【Unite 2017 Tokyo】最適化をする前に覚えておきたい技術 の講演動画を見て勉強用でまとめたメモ。もう3年前の講演ではあるが、2020年でも意味のある内容だったのでまとめた。
Unity 最適化の概要は本記事をざっくり流し読みして、詳細は講演動画を見るとより理解が深まると思われる。
最適化の手順
最適化の前に以下を覚えておく。
- 処理の一部分を直せば大体の問題は解決
- 直すべき処理を見極めるのが最優先事項
- いきなり修正作業を始めてはダメ (時間の浪費, 修正前後のパフォーマンス差分が取れない)
実際の手順は下記の通り。
- 処理負荷となっている箇所を特定するためにプロファイリング
- 負荷となっている処理内容が特定できたら修正の算段を立てる
- 実際に作業して直す
プロファイリングについて
- 処理に掛かった時間やメモリ使用状況を確認する作業
- Unity 標準機能の Profiler を使う
- 各プラットフォーマーのネイティブ Profiler もあるが、大まかな把握は Unity の Profiler で充分
- CPU/GPU/Rendering/Memory/Audio/Physics/uNET/Viedo Player 等の状況が確認可能 (実機での負荷も確認可能)
メモリ使用量が多い場合
- 単純にメモリ使用量が多い or メモリリーク
- C#メモリが多い or Unityメモリが多い
C#メモリでの着眼点
- C#スクリプトで利用しているメモリでGCされるが、予約済みメモリは二度と返されない
- C#メモリは Monoを確認する
- UsedよりもReserved(予約済み)に注目
- Editor上だとEditorメモリも加算されるので実機が良い
- Reservedが大きすぎる場合、一時的にC#メモリが膨らんでる可能性がある
- ファイル読み込みや通信で一時的に大量のC#メモリを使用していないか
- 指標として 300MB は大きい
- 大きかったらスクリプトの見直しを行う
Unityメモリでの着眼点
- Texture, Mesh, Animation 等のアセットのデータで利用 (GCされないのでリークする)
- メモリ上にある各Assetのメモリ内の容量をチェックする
- Memory Profiler を Detailed ビューに切り替えて Take Sample を押すと表示される
- どのアセットがどのぐらいメモリを確保しているのかが分かる
- 指標として 10MB のテクスチャは大きいので圧縮する
- 余計なアセットが読み込まれないようにする, 必要以上に大きいアセットは圧縮等を行う
- 必要であれば読み込みタイミングをずらす事も視野に入れる
- 岩のテクスチャなのに「1024 x 1024」みたいにリッチなパターンもあるよ
ロードが長い場合
- 本当にロードが長いのか? => ロード済みデータを確認して、余計なデータや大きすぎるデータが無いか
- それとも初期化処理が重いのか? => C#スクリプトを見直す
- CPU グラフが跳ね上がったところがロードタイミング、ここをクリックして詳細を追う
- スクリプト名.Awake という形で C# スクリプトと対応している
- LogStringToConsole がロード時間の1/4 (Debug.Logの書き出し)
- Debug.logger.logEnabled = false でログ書き出しを無効に出来る
- Texture.AwakeFromLoad で 0.35秒、テクスチャのロードが重い
- CPU 詳細情報を Timeline を見ると、別スレッドで行っているファイル読み込み処理も確認可能
- Memory Profiler の Detailed ビューで読み込まれるはずの無いデータが読み込まれていないか?
ゲームが一瞬固まる場合
- 裏でロード処理を行っている心当たりがない場合は GC が走っている
- C# メモリが足りなくなった時に GC が発生, GC は膨大な処理時間を取られるので固まる
- CPU グラフ で Garbage Collector のみを表示
- 指標として 16ms 掛かってる場合もある
- まずはどの C# スクリプトが多く消費しているのか特定, 可能な限りメモリ使用を抑える
- 文字列操作(連結等)はメモリを都度使用するので、StringBuilderクラスを使うと良い
- 毎フレームのメモリ確保は控えるようにする
- CPUの詳細の GC Alloc にて、C#メモリが確保されたか確認できる
- 指標として 5.6kb は多い
- Deep Profile を ON にすると、更に詳細の呼び出し先の情報が確認出来る (実行中の動作は重くなる)
フレームレートが常に低い場合
- 60FPSにしたいなら1フレームの処理を16.6ミリ秒に抑える必要がある
- 30FPSでも33.3ミリ秒に抑える必要がある (30FPSは許せる, 20FPSを下回るとカクカクしてると感じる)
- ゲームロジックの負荷なのか?それとも描画負荷なのか? (描画処理の修正によるバグ発生は少ない)
- CPUグラフの「Rendering」が描画負荷、それ以外 (Vsyncを除く)ならゲームロジック
- Vsync は指定したフレームレートになるまで処理を待つ機能 (30FPSだけど10FPSで終わったら20FPS待つ)
描画負荷の場合
- PCとモバイル端末では描画性能/解像度が大きく違う (モバイルのほうが問題発生しやすい)
- モバイル端末は FullHD を超える解像度だが、描画性能は低い
- PC は大体は FullHD なので解像度が低いが、描画性能は高い
- 試しに解像度ギリギリまで下げた状態で実機で動かしてみて軽くなったら描画負荷 (Screen, Camera.Rect)
- 描画処理の確認は FrameDebugger を使うと良い、描画順や描画したMaterialの確認が可能
- 余計な描画が入っていないか
- Batch数, Set Pass数 (ドローコール) が多くないか ( 200 を超えたら見直しが必要)
- 頂点数が多すぎないか
- 描画面積が広くないか (Overdrawをしすぎていないか)
- Shader が重くないか (軽いMobile/Unlit/Texture, Mobile/VertexLit 辺りと比べて差分を確認すると分かりやすい)
ゲームロジックの負荷の場合
- 負荷の原因はC#スクリプト?Unity側の処理?
- Unity側の処理は物理処理、UI処理がよくある負荷原因
- GameObject が多すぎて全体敵に重くなることもある (モバイルで上限 3,000個)
- C#スクリプトの負荷: CPUグラフのHierarchyからBehaviourUpdateがどれくらい時間がかかっているか確認する
- 物理の負荷: Physics.Processing を確認する
- UI処理の負荷: Canvas.SendWillRenderCanvases を確認する
- GameObjectの数は Memory グラフの simple に表示されるので確認する
- C#スクリプト: ゲームによって色々
物理処理
- 余計な衝突判定を行っている → Layer Collision Matrix を見直す
- 複雑な衝突判定を行っている → MeshCollider は重いので BoxCollider等 で置き換える
- 1フレームに複数回Physics処理が回っている → TimeのFixedTimeStep設定を見直す (ありがち)
- PhisicsはUnityのUpdateとは別に回っている: ProjectSettings -> TimeManager -> Fixed Timestep が秒間の処理 (30FPSなら 0.33 で充分)
UI処理
- 原因: 重い頂点計算が走っている (canvasは動かすと再計算が走る)
- 対策: uGUIはAnimationしているものは別Canvasに分けないと重い
参考リンク
まとめ
最適化の手順や手法、負荷の原因が体系立って説明されていたので、Unity でゲームを作ったけど重いと感じたが何をしたら良いか分からない人にとっては参考になる内容だと思った。特に、Profiler で見るべき項目に加えて重い処理時間の指標も説明されていたので、今すぐ使える知識になるのでは無いだろうか。
また、上記の他に「Quality設定」や「フレームレート設定」も見ておくと良いかもしれない。
※ 記事内容に間違いや問題が合った場合は、お手数ですがコメント等でご指摘頂けますと幸いです。
- 投稿日:2020-09-18T03:08:05+09:00
【Unity】ボタンを押したら沈み、離したら浮き上がるようにする方法
こんな感じにぽちぽちできるようにしていく。
この記事では細かいことは説明せず、書いてある通りやればボタンをこのようにポチポチできるようになるまでの流れを書いています。まずはヒエラルキービューの「Create」→「UI」→「Panel」の順番でパネルを作る。
その後「Panel」を右クリック→「UI」→「Button」の順番でボタンを出す。
そしたらこんな感じになる。
そしたらButtonを選択した状態でインスペクタービューにある「Button」コンポーネント
ここの「Transition」を「Animation」にし、この画像でいう下から三番目の「Auto Generate Animation」を押す。
そうすると新しくアニメーションコントローラーを作れるので適当に名前を入力し
作成。ここだとそのまま「Button.controller」にした。
今作ったアニメーションコントローラーの中身をみてみるとこんな感じになっている。
自分でアニメーションコントローラーも作っても大丈夫だが、このデフォルトで用意されている形がすぐできるのが個人的に楽と思うのでこのような流れで作った。(状態もアニメーションのトリガーも用意されている為)
そしたらこの↓の画像ような形にする
アニメーションコントローラーについてはこちらを参考にするといいです。
【UnityC#講座】AnimatorControllerの使い方この形ができたら「Highlighted」から「Pressed」に伸びている矢印をクリック。
インスペクタービューの「Condetions」を「Pressed」にする。
こんな感じ。それと同じように「Pressed」から「Highlighted」に伸びている矢印をクリックしインスペクタービューにある「Condetions」を次は「Highlighted」にする。
それができたらプロジェクトビューでアニメーションコントローラーの中にあるHighlightedを選択し、インスペクタービューの「Loop Time」のチェックを外す。
Pressedも同じように「Loop Time」のチェックを外しておく。
これで第一段階終了です。
次にヒエラルキービューにある「Button」を選択した状態でアニメーションウインドウを開き、「Normal」を「Highlighted」にしてください。
その後「Add Property」を押し「Rect Transform」→「Scale」の順番に選択します。
そうしたらこの画像のような感じになると思います。
そしたら赤い丸ボタンを押し、1:00のところにあるダイヤの形をしたものをクリックしながら0:01のところまでドラッグします。
その後白い線が見えると思いますが、それを0:01のところまで移動させます。
この細い白い線は上にある時間が書いてある薄い赤い色の部分でクリック、ドラッグすれば動かせます。
そこで「Scale.x」を1.2「Scale.y」を1.2にします。
できたら最後に赤い丸ボタンを押します。
そしたら赤くなっていた部分が元の状態になると思います。
次は左上の「Highlighted」をクリックして「Pressed」に変更してください。先ほどと同じ手順で今度は0:00のところのScale.xを0.8にしてScale.yを0.8にしてください。
1:00のところにあるダイヤは消してください。
そうすると↓の画像のようになるはずです。
これで第二段階終了です。
次は最後の段階でスクリプト を描きます。
「Create」→「C#Script」でスクリプト を作り名前は「ButtonPushu」としました。
このように書いてください。using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; public class ButtonPushu : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler { Animator _animator; Button _button; void Start() { _animator = GetComponent<Animator>(); _button = GetComponent<Button>(); } void Update() { } public void OnPointerEnter(PointerEventData eventData) { _animator.SetTrigger("Highlighted"); //マウスカーソルが重なったらボタンが大きくなる } public void OnPointerExit(PointerEventData eventData) { _animator.SetTrigger("Normal"); //マウスカーソルがボタンから離れたら通常の大きさになる } public void OnPointerDown(PointerEventData eventData) { _animator.SetTrigger("Pressed"); //ボタンをクリックしたらボタンが沈む } public void OnPointerUp(PointerEventData eventData) { _animator.SetTrigger("Highlighted"); //マウスボタンが離された時の処理 } }中身についてはざっくりクリックの状態(押した、離れたなど)に応じて先ほど作成したアニメーションの処理を実行するような感じです。
スクリプトについてはこちらのサイトが分かりやすかったです.Unityでマウスイベントのインタフェースの実装とレイキャスト
スクリプトができたらこのスクリプトをButtonにアタッチ
終わり。