UIにおいてSetActiveが大量に使用される問題

今回の話題:

1)UIにおいてSetActiveが大量に使用されるなら、どのように最適化するか

2)ASTC圧縮とETC2圧縮によって生成されたAPKパッケージの問題

3)PNG画像形式とTGA画像形式の問題

4)ゲーム実行のクラッシュの問題

5)AssetBundleの読み込み方法に適用できる環境


UI

Q:UIの日常開発では、特定のGOを表示または非表示にするために、多くのSetActive(true)/(false)コードが使用されます。しかし、SetActiveのコストが大きすぎるので、SetActiveを使用したくありません。他の解決策はありますか?

 

A1:この状況には、Active/Deactiveのコストには主にいくつかの側面があります。

  1. C#層からNative層へのシャトルコール速度は、C#層よりも遅くなります。
  2. UI要素を変更すると、所属Canvasが変更され、関数Canvas.SendWillRenderCanvases()およびCanvas.BuildBatch()がトリガーされ、高い時間コストを引き起こします。
  3. UI要素のメッシュ頂点配列を変更すると、MONOメモリの割り当てが発生し、GCがトリガーされ、高い時間コストを引き起こします。(ただし、UI要素の位置を移動したらMONOメモリの割り当ては発生しません)。

したがって、最適化は次の点からも考慮することができます。

  1. C#層で変数を設定して、対応するGOがActive状態かDeactive状態かをマークし、ActiveオブジェクトではSetActive(true)を回避し、非activeオブジェクトではSetActive(false)を回避します。ActiveでSetActive(true)を実行すると、「最下層」が判断を下しますが、コールした際、最下層はすでにC#層からコールされているため、コストが高くなります。 C#層で適切に判断することで、最下層に判断させることを回避できます。
  2. 頻繁に変更されるUI要素とまれに変更されるUI要素を別のCanvasに配置して、UI要素の変更による時間コストを減らします。
  3. UI要素の座標をCanvasの範囲外に移動して表示および非表示にし、SetActiveの時間とSendWillRenderCanvasesの時間を回避します。
  4. テスト後、Componentにenabled = falseの操作を行うことは、GOにSetActive(false)の操作を行うことより時間が少なくかかりません。
  5. CanvasGroupコンポーネントを追加して透明度を設定することにより、表示と非表示を表します。

 

 

A2:最近最適化を行い、この問題も気づきました。特に、ImageとTMPTextがハングアップされている時にSetActive場合、時間コストがもっと長くなります。そして、次の側面から最適化しようと思います。

  1. UICanvasをハングアップものに対し、Layer層を直接変更します。
  2. UICanvasをハングアップしないものに対し、CanvasGroupをハングアップしてAlphaを制御するように変更します。ここにはやや面倒なところがあります。こちらのプロジェクトは長い間開発されたため、インターフェイスの方に、制御が必要なノードにCanvasGroupをハングアップさせることは現実的ではありません。実行時にコンポーネントを動的にハングアップことには、パフォーマンスに関するいくつかの懸念があります。そのため、実行時にActiveの変更が発生するノードに見たら、対応するPrefabにCanvasGroupコントロールを追加することで、PrefabがCanvasGroupコントロールをうまく補足するようになります。正式なリリース後、欠落がない場合は、SetActiveを直接使用してください。
  3. CanvasGroup方法は欠点があります。つまり、Alphaだけが変更され、レイアウトは引き続き使用されるため、親インターフェイスはLayoutであり、CanvasGroupは使用できません。最初の計画は、SetScale0とSetActiveの時間コストを比較することです。どちらも、再描画を引き起こすはずです。

Texture

Q:空のプロジェクトで、2048 * 2048の大きな画像をいくつか入れました。ETC24bitsで圧縮した場合、1枚のシートのサイズは2MBで、ASTC6X6で圧縮した場合、1枚のシートのサイズは1.8MBです。

個人的な理解によると、ASTC圧縮形式で生成されたAPKは小さいはずですが、まさか予想と正反対とは思いませんでした。ETC2圧縮で作成されたAPKは21.1MB、ASTC圧縮で作成されたAPKは25.7MBです。

 

ETC2で圧縮されたパッケージが小さいのはなぜでしょう、ASTCによって作成されたAPKは大きいのはなぜでしょうか?

 

A:占有されているパッケージのサイズと、EditorのPreviewインターフェイスに表示されるサイズは異なるものです。

Previewインターフェイスに表示されるサイズは、ASTCまたはETC2のアセットのサイズであり、パッケージ化後、そのアセットはさらに圧縮されます(LZ4またはLZMA)。 LZ4に圧縮されたETC2が占めるパケットのサイズは、LZ4に圧縮されたASTCが占めるサイズよりも実際に小さいとしか説明できません。理由は、特定の圧縮アルゴリズムの実装によって異なります。

AssetBundleを使用して確認できます。AssetBundleパッケージ化時にNoCompressionが選択されている場合、確かにASTC形式はETC2形式のAssetBundleパッケージよりも小さくなります。 LZ4またはLZMA圧縮を選択した場合、ETC2形式のAssetBundleはASTC形式のAssetBundleよりも小さくなります。


Texture

Q:アーテイストによると、透過なチャンネルの画像があり、TGA形式である必要がありますが、透過なチャンネルがないため、PNG形式にすることはできません。Unityで透過なチャネルがあるPNG形式があるのは、なぜですか?どのようにしてアーテイストにPhotoShopでPNG形式の画像を作成させ、それでも効果を満足させますか? Unityの2つの図の違いの原理について詳しく説明していただけますか?

 

A1:PhotoShopにはPNG用の透過なチャンネルはありません。エクスポートしても、RGB3チャネルのみをエクスポートできます。PNGピクセルの透過度情報を変更するには、マスクを使用する必要があります。

アーテイストに、「この情報はマスクに描かれています。黒――透過、白――不透過であるように」のように伝えたら、理解してくれたと思います。

 

 

A2:TGAアーテイストを選んだ方が容易く処理もらえます。PNG形式の違いを気にする必要はありませんから。PhotoShopでAlphaを扱う必要もありません。結局のところ、エンジンはさまざまなプラットフォームに従って圧縮する必要があります。プロジェクトの規模に気にならない場合、より便利なプロセスが鍵となります。


Android

Q:ゲームを一定時間実行すると、以下のように一部のマシンがクラッシュします。皆もこのような状況に置かれることがありますか。

JNI ERROR (app bug): global reference table overflow (max=51200)

 

Unity 2018.3には関連するコンテンツがあると気付いたのです。今使用しているバージョンは2019.4.10で、修正されているはずですが、解答お願いいたします。

 

A1:JNIのコール回数が多すぎませんか?以前に試しましたが、Tencent Voice のAPI JNIを呼び出し続け、一定時間実行するとクラッシュします。問題主のおっしゃったのと同じようです。

 

A2:AndroidJavaClassとAndroidJavaObjectが頻繁にNewだけで、Disposeを呼び出さずにが発生するはずです。

 

A3:次の情報を参照してください。

2019.4.21f1 Release Notes
Fixes

  • Android: Fixed Java local reference leaking when using AndroidJavaClass/Object. (1283209)

 https://unity3d.com/unity/whats-new/2019.4.21 のFixesに載っています。

 


AssetBundle

Q:AssetBundleロード方式の適用可能な環境に関して、AssetBundle.LoadFromMemoryおよびAssetBundle.LoadFromStreamの適用可能な環境は何ですか?

A1:AssetBundle.LoadFromMemory

  1. UnityWebRequestによってダウンロードされたAssetBundleアセットを使用し、使用後にローカルに保存しないでください。
  2. 暗号化要件のあるAssetBundleアセット。

AssetBundle.LoadFromStream

  1. 暗号化要件のあるAssetBundleアセット(メモリ値は理論的にはAssetBundle.LoadFromMemoryよりも小さいです)。
  2. Androidでは、StreamingAssetsディレクトリからコピーしてストリームを作成する必要があります。

 

A2:アセットを暗号化する必要がある場合は、最初にAssetBundleをメモリに読み込み、復号化してから、AssetBundle.LoadFromMemoryを呼び出してロードすることができます。この方法のメモリの最大使用量は、少なくともAssetBundleの2倍になります。「AssetBundleの原則とベストプラクティス」(中国語注意)を参照してください。

AssetBundle.LoadFromStreamはストリーミングモードでロードできます。すべてのAssetBundleをメモリに読み込んでから復号化してロードする必要はありません。代わりに、Bufferのようにその中で一部を読み取り、復号化することでロードできます。その方法はあまり大きなメモリを使用しませんから。このインターフェイスを使用する場合は、継承されたFileStreamクラスをカスタマイズしてから、ReadとWriteの方法でByte配列に対して、暗号化および復号化処理を実行する必要があります。

具体的な使用法については、https://www.xuanyusong.com/archives/4607を参照してください。


UWA公式サイト:https://jp.uwa4d.com

UWA公式ブログ:https://blog.jp.uwa4d.com

UWA公式QAコミュニティ(中国語注意)https://answer.uwa4d.com