Unityのクリッピング規則
今回の主な話題:Unityのクリッピング規則、キャラメイク機能の実現方法、Graphics.Bilt時間コスト分析、携帯をロック解除してゲームに戻る時ゲームUI が変形する。
製作
Q1:Unityバージョン5.5.4f1、設備はHuawei Honor v10、Androidバージョン8.0、水平画面ゲームが支払いために垂直画面に引き上げた後、水平画面に戻る時ゲームの解像度がエラーになりました。画面の半分が黒ですが、NGUIクリック位置は正常です(レンダリングされた位置ではなく本物の位置にあります)。
Unity 4.xでもこの問題が発生しました、Androidバージョンも8.0。ログを確認して、横と縦が外部から変更されていることがわかりました。C#レイヤーで解像度を設定しようとしても解決策はありません。最後に、UnityのUnityPlayerNativeActivityファイルを変更し、Androidレイヤーの解像度をリセットしましたが、現時点では他の悪いところは見つかりませんでした。
@Override protected void onCreate(Bundle paramBundle) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(paramBundle); getWindow().takeSurface(null); getWindow().setFormat(PixelFormat.RGBX_8888); this.mUnityPlayer = new UnityPlayer(this); setContentView(this.mUnityPlayer); this.mUnityPlayer.requestFocus(); saveWidthHeight(); } @Override protected void onResume() { super.onResume(); setLayoutParams(); this.mUnityPlayer.resume(); } private void setLayoutParams() { if (Build.VERSION.SDK_INT >= 26) { //android 8.0 mUnityPlayer.getView().getLayoutParams().width = width; mUnityPlayer.getView().getLayoutParams().height = height; } } private void saveWidthHeight() { if (Build.VERSION.SDK_INT >= 26) { Display display = getWindowManager().getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); display.getRealMetrics(dm); width = dm.widthPixels; height = dm.heightPixels; } }
アセット管理
Q2:Unityのクリッピング規則について教えてください。2015年初にIL2PPが実装されたばかりの時にはこの問題をよくみられました。
今までもUnity公式から明確な規則を定めていませんが、大まかな解決策はlink.xmlを追加することです。いくつかのプラグインの解決策はこちらに参考できます:http://forum.arongranberg.com/search?q=il2cpp
少し前に、SLuaホワイトリストのクリッピングを行っているときに同じ問題に遭いました。 下記の部分を理解しました:
◎link.xmlが指定したのはクリップされません(ただし、ワイルドカードがサポートされているとドキュメントに記載されていますが、試したけど出来ませんでした)
◎パッケージが依存するアセット(つまりEditor.logにある部分)は自動的に分析されます
◎PreserveAttributeマーク
最も厄介な部分は、AssetBundleにパッケージされたアセットです。AssetBundleに入力されたアセットにはAnimator Controllerがありますが、外部のResourcesなどにはそのようなものはありません。だからデフォルト状況でAC対応類別はクリップされますが、実行時に必要がありますから崩壊してしまいました。
解決ために自分がYAMLを分析することができます、こちらを参照して:https://docs.unity3d.com/Manual/ClassIDReference.html
PS:実にはACは最も妙なものであり、この対応類別はまだわかりません。今は回避のためにResourcesの下に一つの空きACを配置します。
製作
Q3:ゲーム内のキャラメイクの具体的な実現方法について聞きたいです。ネットで調べましたけど、相関資料は少ないです。ゲーム「天涯明月刀」に関する記事一編とHoney Selectを紹介する記事一編のみ検査しました。それを読んだ後もまだ不明な点が多いと感じます。参照用の相関オープンソースコードがあるかどうかを知りたいです。
君が話したのは逍遥剣客さんが書いたこの文章:https://zhuanlan.zhihu.com/p/28471808(中国語注意)
実際には、Honey Selectは暗号化されていません。UnityStudio+dnSpyで直接開けます。
入社後の最初のシステムはキャラメイクでした。もちろん当時はKang Laiさんほどの豊富なゲーム経験はなかったので、参照したのは基本的に社内の情報でした。ほとんどの機能は「Honey Select」にあります。まとめると:
1.骨格に基づいて調整します。
骨格のScale、Rotationを調整し、Positionは比較的使用されていません。Kang Laiさんのブログにもう非常に詳しく説明されているため、これ以上は説明しません。
2.メッシュの差に基づいて。
この部分は、前に言及されていない部分です。骨の数が制限されている場合、または骨がうまく作成できない場合には、最大と最小の2つの極値メッシュが作成され、両者の差より中間メッシュ値を計算します。そして実行時に最終表示されるメッシュを生成します。骨格調整との違いは、骨格アニメーションとメッシュアニメーションの差と多少似ています。
3.材質に基づいて。
メッシュが変更されていない場合、肌の色、瞳の色、唇の色、タトゥーのデザインなど、材質のテクスチャまたはパラメータの変更を通じて変更できます。(UVの変更で、タトゥーの色などをカスタマイズすることができます。)
4.新しいメッシュ。
この部分は「Honey Select」にも言及されています。プログラムにとして最も簡単な部分でありますが、アーティストにとして最も複雑な部分です。ヘアスタイル、ファッション、他のエクストラアクセサリーなどに使用されます。
検索キーワードについては、character customizationなどを参照できます。
ちなみに、キャラメイクシステム実行中の効率を保証すると共に、プレビューの速度を確保する必要もあります。時々にこの二者の間に衝突が発生する場合があります。多法案サポートやメモリを効率に交換するなどの方法で最適化できます。モバイルゲームの場合は、極端状況でのレンダリング効率とメモリコストを注意することをお勧めします。材質パラメータの変更がある場合、バッチ処理することはできません。骨格またはメッシュに基づく変更も、一定量のメモリを消費します。
レンダリング
Q4:下記の画像はUWAのパフォーマンスレポートです。Graphics.Blitのコストが小さくないことがわかります。これは何のためですか?オフにされませんか?
カメラのHDRはデフォルトでオンにされています。このチェックを外すと消えます。通常に、あの位置のGraphics.Blitは、 スクリプトによって追加されたカメラ後処理ではなく、HDRまたはAAなどのような、エンジン自体が開いた後処理であります 。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com
ShaderLabのメモリ最適化
今回の主な話題:ShaderLabのメモリ最適化、Static Batchの使用に関する注意事項、foreachのMonoメモリ割り当て問題の修復、Unity 2017のシーンライティングのベイク情報が大きすぎ。
レンダリング
Q1:Unity 2017で、社内のアーティストがベイクされたシーンライトマップには、Lighting Data Assetなどのファイルも含まれています。検索したらスタンプのサンプリング情報などのデータが含まれていることを示しています。大きくすべきではないですけど、現在のプロジェクトでは、このファイルは90MBに達し、シーンは非常に異常です。これをパッケージしなくで、インデックスから削除することを提案する人もいますが、そうすればシーンのライトマップは正常にロードできるのでしょうか。
こちらで検査してください。下記のような文があります。
The asset is an Editor only construct so far, so you can’t access it in the player. Currently, this file is a bit bloated as it contains data for multiple platforms - we will fix this. Also we are considering adding some compression for this data.
ですから、90MBのすべてがバッグに入れられたわけではありませんが、中に保存されている必要な情報がバッグに入れられた後、具体的にどの大きさになるのは言いにくいです。一般的に、Precomputed GIを開かない場合には大きくないですが、「インデックスから削除」してパックすると、理論的にはライトマップが失われるので、動的にロードして設定する必要があります。
メモリ管理
Q2:実機のテスト環境で、プロファイラーのメモリ使用状況を確認したところ、ShaderLabが大量のメモリを占有していることがわかりましたが、中に何がありますか?シェーダーバリアントを削減した後、この部分のメモリは減少し、Unityに組み込まれたStandard Shaderは使用されなくなりました。バリアントを減らずに、この部分が占めるメモリを減らすことはできますか?
また、同じアイテムの下にあるオブジェクトが多くを占めていることもわかりましたが、なぜですか?
ShaderLabは、プロジェクトがShaderをコンパイルするときに生成した分析バッファーです。このメモリ増加の特徴は、キーワードとバリアントが多いほど、メモリ使用量が大きくなることです。現在、バリアントを削減せずにメモリ使用量を削減する効果的な方法はありません。後で見つかったら、この部分を更新します。
オブジェクトについては、検索して、前の関する答えを調べるとお勧めします。例えばこちらに:https://answer.uwa4d.com/search/objects(中国語注意)
アニメーション
Q3:次の図に示すように、精度処理のためにアニメーションサフィックスファイルをインポートすると、一部の情報が失われました。
変更されたコードは下記のように:
AnimationClip theAnimation = AssetDatabase.LoadAssetAtPath<AnimationClip> (path);
Keyframe key;
Keyframe[] keyFrames;
foreach (EditorCurveBinding binding in (AnimationUtility.GetCurveBindings(theAnimation)))
{
AnimationCurve curve = AnimationUtility.GetEditorCurve(theAnimation, binding);
if (curve == null || curve.keys == null)
{
continue;
}
keyFrames = curve.keys;
for (int i = 0; i < keyFrames.Length; i++)
{
key = keyFrames[i];
key.value = float.Parse(key.value.ToString(“f3”));
key.inTangent = float.Parse(key.inTangent.ToString(“f3”));
key.outTangent = float.Parse(key.outTangent.ToString(“f3”));
keyFrames[i] = key;
}
curve.keys = keyFrames;
theAnimation.SetCurve(binding.path, binding.type, binding.propertyName, curve);
失ったのはmEditorcurvesです。このフィールドはanimがEditorに占めているメモリに影響しますのみ、実機で実行中のアニメーション効果とメモリ占有には影響しません。ですから、フィールドリストが空であるかどうかは、実際のデバイスでのパフォーマンスに影響を与えることはなく、質問を行う方法で正確に処理できます。このフィールドを保持したい場合(実機のメモリには影響はありませんが)、テキスト処理によってアニメーションファイルに対して精密処理を実行し、処理中にmEdtorcurvesフィールドをスキップできます。
アセット管理
Q4:シーンにStatic Batchを使うと、使用されているすべてのモデルが大きなメッシュに合併され、ファイルとして保存されますので、シーンのAssetBundleが大きくなり、シーンのモデルファイルとファイルリソースが重複しているようになります。
もしシーンがStatic Batchをマークしなくて、ロード後にStatic Batchを動的に設定する場合、サイズを縮小できますか?
そうです、Static Batchをマークすると、追加のCombined Meshデータが生成され、常により大きなサイズとメモリを占有します。Static Batchをマークしなくて、ロード後にStaticBatchingUtility APIを使って動的に設定するのも良い選択です。唯一の違いは、スペースコストの代わりにシーンのロード時間がかかっていることです。
アセット管理
Q5:Unity5.6.4でforeachが引き起こすMonoメモリの割り当て問題が解決されましたか?
Unity 5.6では、確かにforeachはMonoメモリの割り当てを生成しません。さらに、王さんのサプリメントのおかげで、この問題はUnity 5.5.5p1でも修正されました。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com
AnimationClipのABファイルサイズ
今回の主な話題:AnimationClipのABファイルサイズ、異なるUnityバージョンのABファイルサイズは不一致、ABの前後にパックされたCRC値は不一致。
アセット管理
Q1:最近パッケージの圧縮を行っています。一部のアニメーションファイルのAssetBundleファイルが比較的大きいことがわかりました。その原因は含まれているクリップの数が多いですから。しかし、これらのクリップはすべて再利用され、ただアニメーショントラックに掛かっているイベントが異なるから、作ってあげます。
LZ4の圧縮形式を使用し、ファイルのサイズは約800K +です。偶然にLZ4以外の形式を使って、このファイルのサイズは約60Kです。違いは10倍以上ので、おかしいです。
他のプロジェクトでこのような問題が発生したかどうかはわかりませんが、 理論から理解してみました:
このような繰り返しアニメーションでは、データを再利用でき、アニメーションイベントは追加の配置を保存することと同じであるため、理論的には、このような再利用されたClipsを追加しても、AssetBundleファイルに大きな影響はありません。
私の推測が正しいですか?この場合、この部分の体積を圧縮するための良い方法はありますか?
この問題の存在可能性があります。 LZ4圧縮を採用しているアニメーションファイルAssetBundleのサイズは、アニメーションパネル内のClips数と各Clipsのサイズに関連あります。
ソースコードが見えないので、自分の見解を述べます:AssetBundleをパッケージするときにLZ4圧縮を選択すると、アニメーションファイル内の各Clipが独立アセットとして圧縮され、解凍時に必要なClipだけを解凍するようになれます。ただし、こうすれば異なるClip間の重複データの再利用を遮断しますので、AssetBundleの体積がClip数の増加により大きくなります。簡単な例は、同じClipを複数追加した場合、Clip数が増えるにつれてAssetBundleファイルも増えることです(アニメーションFbxのサイズを超えやすい)。
デフォルトのLZMA圧縮では、AssetBundleが一つの全体として圧縮され(もちろん、解凍するときも全体的に解凍する必要があります)、異なるクリップ間で繰り返されるデータは完全に再利用できます。それで、圧縮されたAssetBundleのサイズは分割されたClipへの影響は大きくないです。同じ例で、アニメーションファイルに複数のClipを追加した後、LZMAで圧縮したAssetBundleのサイズはまだ小さいです。異なるClipの数を増加しても、サイズは基本的に同じままです。
そうしたら、圧縮によってアニメーションファイルAssetBundleの体積を削減したい場合、解凍速度に影響小さいアニメーションファイルにLZMAを使用して圧縮できます。
アセット管理
Q2:Unity 5.3.8と5.6.5で同じ設定を使用しましたが、FBX後のサイズの差が非常に大きいのはなぜですか?
Unity 5.3.8 :35K,
Unity 5.6.5 :400K。
問題主のAssetBundleファイルを見て、下記の手がかりを提供できます。
Unity 5.6のBundle:
Unity 5.3のBundle:
二つのBundleの違いはShaderにあることがわかりました。そしてUnity 5.5アップデートに次の文が確認できます。
Shaders: Shaders are now exported to the Unity player completely in binary. There is no Shader text string and parsing in run time.
つまり、Unity 5.6のBundleのShaderはバイナリにコンパイルされていますが、Unity 5.3のBundleはまだテキストのShaderであり、Bundleの違いにつながります。
アセット管理
Q3:シーンをパッケージする後のAssetsBundleが毎回異なります。Strippt Shaderの設定も変更し、毎回同じ空シーンにパックしましたが、パックされたCRC値が違うです。
最初にローカルリソースを削除するたびに、SVNからCheckOutしてパッケージします。シーンに使用されるシェーダーは独立のBundleにパッケージし、このシェーダーパッケージも異なります。また、Unity 5.6.2のパッケージAPIは、ただ一つのBundle Nnameによってパッケージされた関数です。パッケージするほどにシーンを切り替えることができません。
まず、同じリソースが複数回パッケージする場合、確かにMD5は異なります。この問題は以前のUnityのロードマップで言及されていましたが、現在は見つかりません。新しいバージョンで解決される可能性があることを示しています。
しかし、以前のバージョンでは、シーンのAssetBundleを引き起こす可能性のある2つの既知のMD5変化があり、1つは問題主が言及された問題(Shader Stripping)で、もう1つはStatic Batchingを開いた後の毎回パッケージ時に生成されたCombined Mesh(またはその順序)は異なる状況があります。もちろん、他の理由があるかもしれません。
だから、AssetBundleのHash値によって内容が変更されたかどうかを判断することをお勧めします(Shader Strippingの問題によりHashが変更しますが、Combined Meshの問題によりHash値が変更されません)、またはAppendHashToAssetBundleNameオプションを使用して直接Hash値をAssetBundleのサフィックスとしてチェックできます。
その他
Q4:すみません、WWWまたはUnityWebRequestがドメイン名を介してAssetBundleをダウンロードするときに、どうすればアセットの実際のIPアドレスを知れますが?
Dns.GetHostAddressesを使ってIPが変更しているため、WWWが実際に接続しているIPが前回Dns.GetHostAddressesで取得したIPと保証できません。やりたいのは、logクライアントが異なる地域で接続するCDNノードIPとアセットのダウンロード速度を記録することです。
本来でも、ドメイン名とIP自体は1対1の関係ではありません。一つのドメイン名は、複数のAレコードまたはCNAMEレコードを解析できます(CDNなど)。
Dns.GetHostAddressesが取得するのは、ドメイン名に対応するAレコード情報です、digコマンドの出力を参照できます。一般的に、返されたIPをトラバースして、個別に使ってみられます。
WWWまたはUnityWebRequestの内部はどうやってこれを実現とは不明ですが、取得された最初のIPのみを処理する、または成功まで各IPを試行し続き可能性があります。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com
iOSでのLuaのffiパフォーマンス
今回の主な話題:iOSでのLuaのffiパフォーマンス、Shader.CreateGPUProgram 最適化、VRデバイス最適化。
レンダリング
Q1:このゲームでは、キャスト範囲を表示するためにプロジェクターを使用する必要があります。画像の端に数ピクセルを透明にしても問題ありませんが、こうすれば実際の範囲が表示と異なる場合があります。投影された画像が端を離れない場合は、十字が顕示されます。 良い解決策はありませんか?使っているのはUnity5.6.2です。
テクスチャサンプリング時に、テクスチャ座標が[0、1]の範囲を超えると、最も近い(0または1)テクスチャピクセル、つまりテクスチャのエッジにあるピクセルが取得されます。 このとき、テクスチャのエッジに1〜2ピクセルの空白(alpha= 0)がある場合、空白(alpha= 0)ピクセルが取得されます。そうでない場合は、上記の状況になります。
問題主にお勧めするソリューションは二つあります。
1)テクスチャの周囲に1〜2ピクセルの空白スペースを残し、Projector sizeを設定するときに残されたピクセル数によりProjector sizeをscaleします。そうすればscale後の可視部分は、要求と合うsizeであり、範囲の不正確問題を修正できます。
2)Builtin Shaderを変更します。テクスチャサンプリングの時判断をします、テクスチャ座標が[0、1]を超える場合は、alphaを0に設定します。
ロード
Q2:UWAパフォーマンスレポートにShader.CreateGpuProgramの時間コストはとても高いいと見ましたが、これを最適化できますか?
Shader.CreateGPUProgramが1〜2回しか表示されない場合、問題は大きくありませんが、頻繁に表示される場合は、厳密な最適化が必要です。Shader.CreateGPUProgramの最適化に関しては、主に下記の点に分けることができます。
1)プロジェクトがResources.Loadを通してロードされる場合、この二つの方法て実現できます。
●よく使うシェーダーを「Always Included Shader」に入れ、ゲームがスタート時に直接ロードできるようにします。
●よく使うシェーダーを「Shader Preloading」に入れ、ShaderVariantCollectionの形式でロードします。利点は、動的ロードの時間とロードされるキーワードを指定でき、便利になれるだけでなく、ロード時間も短縮できます。
上記のはShader.Parseの作業を完了させだけで、Shader.CreateGPUProgramに対しては、ロードしてインスタンス化した後、カメラのビューボリュームに1フレームを直接「レンダリング」できます。こうすれば、Unityエンジンがトリガーされ、CreateGPUProgram操作が実行され、実行中のコストを前にさせます。通常、この操作はゲームのロード中またはシーンの切り替え中に実行することをお勧めします。
2)プロジェクトがResources.Loadを通してロードされる場合、AssetBundle.Loadで直接にShaderをロードして、Shader.Parse操作を完了させます。この後の作業は上記と同じです。
注意:上記の方法はUnity 5.0以降のバージョンにのみ適用であり、ロード後にシェーダーをメモリに常駐させる必要があります。その後アンロードすると、Shader.ParseおよびCreateGPUProgram操作が再度トリガーされます。
レンダリング
Q3:「Lua + Unityを上手に使い、パフォーマンスを飛ばせよう-LuaJITパフォーマンスピットの詳細説明」(中国語注意)という記事を読みました。ここでは、「LuaJITはiOSでサポートされていませんが、Interpreterモードは使用できます。」と書いてあります。文章に「注意すべきのは、ffiはJITがオンになっているときにのみパフォーマンスを発揮できます。iOSの場合、逆にパフォーマンスを低下させます。そのため、使うときはすぐにオフにする必要があります。」という言葉があります。
私の質問:
- IOS上のJITインタープリターはネイティブLuaよりも早いですか?
- Interpreterモードでffiを使ってCライブラリを呼び出すと、ネイティブLuaよりも遅くなりますか?
Cでバイナリプロトコルを分析したいと思います。たとえば、LuaでCをreadIntやreadShortします。Lua自身はビット操作をサポートしていませんが、Luaのビットライブラリを使ってビット操作を行い、 パフォーマンス分析が非常に遅いため、ffiを使って実現したいです。もう一つCをコール方法もあります、これはRequire Cダイナミックリンクライブラリです。iOSでのffiのパフォーマンスが良くないから、これが最優の選択ですか?
Yunfengのpbcなど、インターネットでは多くのLua関連のプロトコル分析ライブラリがあり、基本的には製品レベルの可用性に達しており、直接参照または使用できます。これらのライブラリのほとんどは、Luaのフィールドごとに展開していませんが、Cによって均一に展開するおり、lazy initの最適化をするかもしれません。フィールドごとにLuaでC function展開をコールすれば必ず言語の相互作用に多くの時間を使います。
ffiをJITから分離すると、パフォーマンスが大幅に低下し(10倍レベル)、大規模なプロトコル分析に使用することは推奨しません。ただし、一般に、クライアントのプロトコル分析はボトルネックではありません(大きなプロトコルをデザインできない、例えばログインによって発行されたユーザーデータなど)。サーバーに対して、JITをオンにできるから、パフォーマンスははるかに高くなります。また、JITするかどうかも構わず、ffiはいづも「メモリが小さい(c structと同じくらい)」という利点があります。
ただし、ffiを選択することはLuaJITをバインドすることと同じであるため、注意すべきです。パフォーマンスの原因で、言語標準、保守性、およびコミュニティ活躍度三つの方面にも、LuaJITはLua5.3ほど良くありません。
VRパフォーマンス
Q4:私たちのプロジェクトはVRに基づいており、単一の建物を作成しますため、2000〜3000面ぐらいは必要です。
アーティストはシーン内の 建物を手動で合併し、同じタイプに基づいてマージします。位置は特定の距離がありますので、カメラが合併後ある建物が見えますが、他の建物が見えない状況もあります。
手動で合併した後にSimpleLODスクリプトを追加し、合併された各メッシュでLODを行い、静的に設定し、実行時にUnityをStatic Batchします。これは、実行中には静的なバッチ処理効果ですが、近所の小さな町の家は、非常に遠い鉱山のいくつかの柱と合併して大きなメッシュになります。
質問は下記のように(読みやすいのため、質問と回答は以下にリストされています):
Q4.1:私たちのシーンは正確にオクルージョンのあるように特別に設定されています。オクルージョンカリングがレンダリングの最適化に役立つはずです。Cullingの効率を向上させる方法は?
A:実際、カリングのレンダリング効率を向上するため、レポートでカリングのボトルネックを確認する必要があります。
次の図は、問題主が今回のレポートの具体的なCulling時間コストを確認するとき(コード効率ページで確認できます)、パーティクルシステムの時間コストは高いとみられました。
次の図は、パーティクルシステムの具体的なCulling時間コスト状況です。パーティクルシステムのレンダリング数を制御することをお勧めします。
Q4.2:同じタイプのモデルを使って手動で合併することは正しいですか?
A:Offlineで合併する可能性はあり、間違った操作ではありません。しかし、Offline合併で視野以外にあるオブジェクトをあまり多く計算に取り入れないように注意する必要があります。これは実際には非常に難しいので、一般的にStatic Batchingの方はより良い選択です。
Q4.3:そのような静的なバッチ処理の結果は正しいですか?柱が鉱山で見られたときに町のすべての建物がレンダリングされ、レンダリングがCPU時間コストを高すぎになさせませんか?現在、DrawCallのピーク値は305で、三角形の数のピーク値は640,000です。Xiaomi 5以上のスマートフォンを対象としている場合、それはCPU時間コストに影響を与える鍵ですか?Xiaomi VRはrtで実装されているため、2つの目が2回レンダリングされます。 しかし、アーティストはアートアセットの精度を直す気もありません。
A:60万のパッチ数は確かに高いです。2つの目があるため、シングルでは30万のレンダリングパッチになります。このモバイルデバイスに対してはまた発熱しやすいです。Unity Editorでチェックし、問題2に述べされている合併しすぎ状況は実際に存在しているかどうかを確認してください。図から、Combined MeshはStatic Batchingで合併することがわかっています。これは大丈夫です。つまり、レンダリングの時、視野以外にあるMeshはレンダリングに参加しません。
Q4.4:たとえば、クライアントが30msまたは40msごとに更新されるなど、設計されたフレーム更新間隔を増やす必要がありますか? それは、CPU時間を削減する方法が他にないためです。
A:下図からわかるように、UWA-FrameCheckHandle-FrameFastForwardでもUWA-CVRRaceScene-StepUpdateでも、主な時間のかかるボトルネックはこれら3つの関数、特にUWA-CVRRaceScene-ObstacleManager.updateです。これについては、問題主にこの関数を分析し続け、時間コストのボトルネックを正確に特定し、最適化の案を確定するとお勧めします。フレーム制御でコストを低下させるのは仕方がない方法です、具体的な時間間隔は適切なパラメータを見つけるために多くの実験を必要とするかもしれません。ただし、これは本質的な問題を解決するものではなく、本質的な問題は上記の方法で特定して解決する必要があります。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com
あなたのモバイルゲーム開発の最適化時間を数ヶ月節約する方法(メモリ篇)
みなさん、こんにちは。こちらはUWA Technologies(略称UWA)です。
今日は「あなたのモバイルゲーム開発の最適化時間を数ヶ月節約する方法」の最後のパート「メモリ」についてご説明いたします。
前回の文章をまだ読まれていない皆様は、是非移動してご覧ください。
「あなたのモバイルゲーム開発の最適化時間を数ヶ月節約する方法(レンダリング篇)」
「あなたのモバイルゲーム開発の最適化時間を数ヶ月節約する方法(UI+ロード篇)」
メモリ
メモリパートに最も重要なのはメモリの割り当てとメモリリークです。
ほとんどのプロジェクトでは、アセットメモリとMonoメモリという2つの大きなメモリ割り当てがあります。 ここでは、最初にアセットメモリについて説明します。
アセットメモリ
UWAレポートでは、テスト中にテクスチャメモリの割り当てを記録できます。 大事なのは、Monoメモリとテクスチャフォーマットです。
また、メモリの詳細を知りたい場合は、クリックして詳細を表示できます。
実例
これはゲーム「Kiwame」からのデータです。テクスチャの詳細情報を見ることができます。 この中に、「アセット名」、「メモリ割り当て」、「解像度」、「フォーマット」などが含まれます。NewUI1_rgbが16 MBで非常に大きいことがわかります。 開発チームはそれを チェックして、最適化する必要があるかどうかを確認できます。
また、アセット管理にも注意する必要があります。表から1つ以上のアセットを選択、それらの使用情報がチャートに表示されます。アセットがいつロードされ、いつアンロードされるかを確認でき、メモリリークを分析することには非常に有用であります。
メッシュ
次にメッシュの詳細を見に行きます。
アニメーションクリップ、オーディオクリップ、シェーダー、フォント、RT、パーティクルシステムなど、すべての詳細情報を直接かつ迅速に見ることができます。
結果
これは 、テクスチャ、およびオーディオの最適化の結果です。
Monoメモリ
二番目に大きいメモリはMonoメモリです。
このように、全体的なMonoメモリ割り当てに注目すべき、メモリの合理性を確認する必要があります。
UWAレポートは、関数のコールスタックと詳細なメモリ割り当てを示します。それで、重要な関数名をすばやく確認できし、コードで検索して最適化することができます。
結果
こちらは結果です。25日をかかって、Monoメモリは150MBから50MBに最適化されました。
メモリリーク
メモリ部分に、非常に難しい難問があります。それはメモリリークです。以前には、メモリリークを分析して解決するには、2〜3か月は常に必要でした。
UWAレポートでは、常駐Monoメモリを表示できます。グラフにMonoメモリがこのように増える一方で、下がらない場合には、つまり、メモリリークが発生したということです。
じゃあどうすれば修正できますか? チャートの2つのサンプルを比較して、Monoメモリの増分を見つけます。
このプロジェクトでは、メモリのほとんどの増加が、InstantateGamObjectという関数のInstantiateによるものであることがわかります。
これらは、garbage collectedできない変異体です。 そして、開発チームは関数を直接チェックして、どのコンテナーがそれらを参照しているかを確認できます。その後、コードをすばやく最適化できます。
結果
これが結果です。 3日後、このプロジェクトでは、メモリリークの問題がほぼなくなりました。
これは別の例です。 ただ1日で、メモリリークの問題が改善しました。
現在、中国でUWA GOTを使っているゲームプロジェクトは、メモリリークの問題は約3日で大幅に改善できます。
過去のメモリリークの割合は約50%でしたが、昨年、UWA GOTでMonoメモリ分析を開始して以来、割合は25%に低下しています。
今回、UWA GOTが日本のゲーム開発者たちに役立つことを願っています。
UWAおよびUWA GOTについてもっと了解したい場合には、UWA公式サイトに移動してください。
UWA GOTを使っていたnowsprintingさんも自身のブログでUWA GOTについて詳しく説明してしましたが、興味があれば「やらなイカ?」に移動してご覧ください。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com
Device.Present時間コストの最適化
今回の主な話題:「Device.Present時間コストの最適化」、「PCでアート特殊効果を検査する」、「LuaJitバイトコードのプラットフォーム相関性」、「Shaderを使用して特定色相の変換を実現する」。
パフォーマンス
Q1:Androidモバイルゲームプロジェクトが実機でProfilerを使ってパフォーマンスを分析する時、CPU消費におけるDevice.Presentインターフェイスの割合が非常に高いため、レンダリング時間が長くなり、レンダリング時間の曲線がグリッチ形状を示している。
一部の情報を確認したところ、垂直同期をオンにしたことが原因との声もあった。 このオプションはオフにすると、この現象は少し改善されましたが、まだ存在している。ある人も全画面UIが原因であると認めたが、 このプロジェクトには存在しない。これの原因と対処方法を誰かが知っている人がいるか。 どうもありがとうございました!
問題主が言った二つ共にDevice.Presentの高い時間コストを導く可能性がある。モバイルデバイス自体が垂直同期をオンにするため、実機で垂直同期をオフにしても実際には役に立たない。
このアイテムは2つの理由で高すぎ:
(1)当該フレームのCPUコストが33ms以下の場合、Device.Presentは基本的にハードウェアの垂直同期が原因だ。
(2)当該フレームのCPUコストが33msより高い場合、高すぎるDevice.Presentは、 GPUプレッシャーが高すぎるか、ある子スレッドのCPUコストが大きすぎることを示す。
問題主は自分の具体的な要求次第で確認できる。
制作
Q2:PCでアート特殊効果のパフォーマンスを確認するに良い方法はないの?以前には、不細工な方法を使う経験があった。それはシーンに特殊効果をたくさん入れてフレーム数をチェックすることだったが、効果はあまりよくなかった。
①
評価式を使っている。つまり、特定のルールに従って特殊効果全体をトラバースし、節点の属性に従ってパフォーマンススコアを計算している。アセットをスキャンしてExcelに出力してチェックできる。 コードを再利用して エディターに直接入力すると、アーティストはこのスコアを参照できる。精度上の要求は必要ではなく、この方法で90%の問題をすぐに発見できる。仕様を事前に指定し、特殊効果シーンと共に、同時に出現する特殊効果の数とパフォーマンススコアが標準を超えるかどうかを推定することもできる。
②
特殊効果がパフォーマンスに要求高いのはOverDrawの原因だ。PCで確認せず、実機で確認した。
PCでの検査はQian LaiKangさんが提供した、一つのOverDraw監視スクリプトを使った。アーティストは特殊効果を作る時にOverDrawを見ながら、状況に応じて調整できる。
次に、実機パッケージをビルドする時に特殊効果リストを作成し、Major Cityに入り、チャットプログラムでGMコマンドを入力し、特殊効果をトラバースする。各特殊効果は1回から無限回に再生でき、AdvancedFPSCounterプラグインを使って当時の FPS値をファイルに記録される。フレームレートが低い場合の特殊効果を特定し、最適化する。
例えばこのコード:
if (cmd.ToLower().StartsWith("-gm texiaoplay3")) { Toast.ShowTip("アーティストが作った特殊効果をテストします " + cmd); > prEffectTest(3); return true; } private static void prEffectTest(int efCount) { if (BilinCamera.Instance == null || BilinCamera.Instance.player == null) { Debug.LogError("ヒーローは見つからないので、特殊効果はテストできません"); return; } TextAsset conf = Resources.Load<TextAsset>("effeclist"); string[] lines = conf.text.Split('\n'); //先に一回同期ロードします for (int i = 0; i < lines.Length; ++i) { string prefabName = lines[i]; if (string.IsNullOrEmpty(prefabName)) { continue; } } BLDebug.isLog2FileEnable = true;//ファイルを書き始めます GameManager.Instance.StartCoroutine(prEffectTestAsync(lines, efCount)); GameManager.Instance.StartCoroutine(logUsedFPS(efCount)); } private static IEnumerator logUsedFPS(int efCount) { float startTime = Time.realtimeSinceStartup; float crtTime = Time.realtimeSinceStartup; while (crtEffectName.Length > 0 && (crtTime - startTime) < 6000.0f) {//100分以内 crtTime = Time.realtimeSinceStartup; CodeStage.AdvancedFPSCounter.AFPSCounter aFPSCounter = CodeStage.AdvancedFPSCounter.AFPSCounter.AddToScene(); BLDebug.Log2File(TimeUtil.currentSqlTimestamp4LongAdv() + "\tlogUsedFPS" + efCount + "\t" + crtEffectName + "\t" + aFPSCounter.fpsCounter.LastValue + "\t" + aFPSCounter.fpsCounter.LastAverageValue + "\t" + Time.frameCount + "\t" + Time.realtimeSinceStartup); yield return new WaitForSeconds(0.1f);// 0.1秒ごとにFPSをサンプリングします } yield return null; } private static string crtEffectName = "notbegin"; private static IEnumerator prEffectTestAsync(string[] lines, int efCount) { float periodTime = 5.0f; //特殊効果をを1つずつ再生します for (int i = 0; i < lines.Length; ++i) { string prefabName = lines[i]; if (string.IsNullOrEmpty(prefabName)) { continue; } crtEffectName = prefabName; GameObject[] effectGoArr = new GameObject[efCount]; for (int k = 0; k < effectGoArr.Length; k++) { GameObject effectGo = PrefabUtil.loadPrefabToGameObject(prefabName, parentGo); effectGoArr[k] = effectGo; if (effectGo != null) {//はUI特殊効果 GameObject.Destroy(effectGo, periodTime); } else { Debug.LogError("ロード失敗しました???" + prefabName); } } yield return new WaitForSeconds(periodTime); } crtEffectName = ""; BLDebug.UploadTodayLogFile();//その日のログをサーバーに送信します yield return null; }
ログをExcelにインポートして分析すればいい。
③
私たちは2つの部分に分けました:
1.特殊効果のリリースが全体的なパフォーマンスへの影響。これは結論に対する統計である。実機でリリース後のフレームレートの変動を統計する。現在のバージョンでは、シーンにフレームレートの記録を入力する機能だけがあり、主な機能は特殊効果がゲームへの最終的な影響を評価することだ。
2.特殊効果のドローコール、面の数、およびOverdraw影響。現在、この部分の統計データはPCでしか取得できないため、スタンドアロンバージョンではスキルの特殊効果を1つずつ再生するツールがある。上記の3つの指標の評価を統計して、標準を超える部分が毎週の会議にレポートで提出する。
個人的な感覚はアセットの検査はツールで定期的にやる必要があり、継続的な観察は最も効果的な最適化方法だ。
ToLua
Q3:ToLuaフレームワークでコンパイルされたLuaJit32とLuajit64を使用してLuaコードをバイトコードに変更するので、すべてのプラットフォームで2セットのバイトコードを入力する必要があるの?それとも、Androidでは32を使ったら大丈夫?PCとiOSは32と64を使用し、実行時に異なるプラットフォームを区別してから、異なる方法でロードするの?実行時にはIntPtr.Size == 4で区別されるの?
iOSでは32 + 64でなければならない、そうしないと32ビットの古いマシンをサポートできない。エディターでは通常64(ただし、私たちが使っているのはLuaファイルを直接読み取る)、PCスタンドアロンでは32を採用する(ユーザー以外の場合は64でもかまわない)、Androidでは 32のみ。
とにかく、対応関係は
x86 arm→ 32
x64 arm64→ 64
IntPtr.Size == 4が信頼できるかどうかについては、詳細なテストはまだやっていない。自分が作ったプラットフォームに対する判断コードを使っている。(実際には、上記の状況に従ったらiOSのみ判断する必要がある)
制作
Q4:画像の特定の選択範囲の色変換を実現したいと考えている。PSでは、 画像選択でエリアを簡単に選択し、元画像の色相を変更できる。ただし、Unityでも似たようなソリューションを実現したが、実際の効果はPSとかなり異なる。全体が暗くなり、個々の領域が露出オーバーになった。誰かが似たようなShaderソリューションがあったら、参照させていただけませんか。
①
Color Lookup Tableソリューションを使用することをお勧め。 簡単に言うと、一つのテクスチャをルックアップテーブルとして使う。このテクスチャに、各色が変更された後の 色を記録する。この方法は、問題主によって述べられた要求を満たすだけでなく、多くの色補正要求(さまざまなフィルター)も達成できる。
cocos2d時代に使ったから、パフォーマンス上には納得できることがわかった、大きな問題は存在しない。AssetStoreにLUTをキーワードとしてのプラグインリを検索して参考になれる。
Color Lookup Tableの作成とカスタマイズの方法については、説明するのは複雑なので、Googleするとお勧め:P。一つの簡単な方法は、Color Lookup Tableには一つの固定された標準色の元画像がある。この画像をPhotoShopにドラッグし、変更したい色相で変更したら、 変更後の各色相を保存できる。
Unity自身のColorCorrectionLookupを参照することもできるが、これは3Dテクスチャなので、2Dに適用するように変更する必要がある。https://docs.unity3d.com/550/Documentation/Manual/script-ColorCorrectionLookup.html
他のソリューション(Shaderに計算コードを直接作成するなど)については、できないではないが、パフォーマンスはあまり高くない。
②
理論的には、問題主が実現した色変更アルゴリズムはCPUでもGPUでも、アルゴリズムとコードが正しいなら、画像の色変更結果はPSと同じすべきだ。
問題主が言った「全体が暗くなり、個々の領域が露出オーバーになった。」ということは、ゲーム内にライトがある場合に見たことか?それとも、変更された画像を保存して比較された結果なの?ゲーム内の場合は、個別に染色された画像に問題があるかどうかを確認してください。ある可能性はアーティストがdiffuse画像にaoなどの別の情報を書いて、後でシェーディングした画像に問題を導く。ただ保存した画像に問題がある場合には、アルゴリズムを検索し、目的と比較してください、理論的には問題はないはずだ。染色効果を実現したい場合は、 一つのUnityプラグインをお勧め、色々なアルゴリズムが含んである:Texture Adjustments
管理
Q5:ベイクする時にはnormalとtangentをインポートする必要があるが、ベイクが完全した後にはインポートする必要はなくなって、メッシュアセットを最適化できる。しかし、アーティストが再びベイクしたい時には、もう一度開いてnormalとtangentの設定をインポートするのは非常に面倒だ。これに対して適切な処理法がないのか?私が考えられるのはパッケージする時にこのアセットたちを再インポートし、normalとtangentをインポートするオプションを削除して、パッケージして元に戻ることだけだ。
Player SettingのOther Settingsにある「Optimize Mesh Data」オプションをオンにしてみてください。オンにすると、エンジンはリリースまたはAssetBundleパッケージング中にすべてのメッシュデータをトラバースして、過剰なデータを削除する。注意すべきのは、こちらの「過剰な」データとは、レンダリング時にシェーダーが要らないデータはメッシュデータに含まれていること。つまり、メッシュデータにposition、uv、normal、color、tangentなどの頂点データが含まれているが、レンダリングに使わせるシェーダーが必要なのはposition、uv、normal三つだけ。colorとtangentは「過剰な」データとして、エンジンはゲームをリリースするまたはAssetBundleを作成する時、これらのデータを自動的に削除する。ただし、Runtime状況にシェーダーを置換する必要のあるメッシュに、開発チームに特に注意すると提案する。もしRuntimeの時、あるGameObjectにより複雑や多くの頂点属性にアクセスする必要のあるシェーダーを置き換える必要がある場合、実行時に頂点属性のmissingで問題を起こさないために、先にこれらのシェーダーを対応するプレハブにマウントしてリリースすると提案する。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com
絵文字のUGUI処理
今回の主な話題:絵文字のUGUI処理、CreatefromMemoryの使用戦略、Resource.Loadがロードされたときのメモリ変動、Animatorのサンプリング。
メモリ管理
Q1:Unity 5.5.2を使用しています。下の画像は、Redmi 2でテストした8つの特殊効果リソースを示しています。ピーク値は46MBで、その後22.6MBに低下します。このメモリ変動の原因は何ですか?Rendering Batchesが原因ですか?
問題主が提供する例によると、UWAがテストをしました。この問題はUnity 5.5.2のAndroid実機で確かに再現しました。Redmi 2で四つの粒子特殊効果をロードしますと、ロードする前のメモリは:
ロードする中のメモリは:
ロードする後のメモリは:
確かに、Unityのメモリが急速に上昇し、その後下降する「ジッター」現象があります。
これに対して、同じようにRedmi 2で、Unity 5.6.2バージョンの実機テストを実施しました。
ロードする前のメモリは:
ロードする中のメモリは:
ロードする後のメモリは:
まだ、メモリには少量のジッターがありますが、そのジッター範囲は大幅に縮小されています。ですから、R&Dチームに、 同じフレームにあるResources.Loadの読み込み回数で制御するか、Unityの上位バージョンにアップグレードすることをお勧めします。
UGUI
Q2:UGUIの入力フィールドを使用しています。ユーザーが携帯でシステムに付属の絵文字を入力すると、問題が発生し、Input Field枠に異常を引き起こします。たとえば、一つの疑問符があって、削除できません。何かプレーヤーが入力した絵文字を濾過できる方法がありませんか?または絵文字を他の文字に変える方法ありませんか?私は今、正規表現を使用して絵文字表現のユニコードエンコーディングに一致させていますが、このようにすると、カバレッジが不完全な状況はまだあります。
①
現在の解決策は、コンポーネントをInput Fieldに掛けて、類似のコードを使って絵文字を濾過することです。ただし、すべての絵文字はまだテストしていません。すべての絵文字を濾過できるわけではありませんが、エラー報告はされません。
public char ValidateInput(string text, int charIndex, char addedChar) { // 絵文字 if (char.GetUnicodeCategory(addedChar) == UnicodeCategory.Surrogate) { return ‘\0’; } }
②
UWAはUnity 5.5.5p2でこの問題を再現しました。しかし、Unity 5.6.2ではそのような問題はありません。アップグレードは実際にリスクをもたらす可能性があるため、あるアドバイスは正規表現を使うことです。この方法も一般的にインターネットで使用されている方法です。
二つ目は、Unity 5.5.5p2とUnity 5.6.2のUGUIコードの変更を比較し(主にInputField)、自分で修復してみます。UnityEngine.UI.dllも自分でコンパイルします。
アニメーション
Q3:Unity 4.x Animatorはエディターでサンプリングを実行できますか? たとえば、スキルアクションは30フレームです。各フレームのモデルの各ノードの座標回転とスケーリングを知りたいのですが、どうすればよいですか?
1)アニメーションウィンドウで直接表示できます。
2)APIを利用します:AnimationUtility.GetAllCurvesまたはAnimationUtility.GetEditorCurveを使用して、各ノードの各属性AnimationCurveを取得して、各AnimationCurve.keysにある各keyframeのvalueを取得します。
UWAブログには、アニメーションサンプリング、GPUスキニング(中国語注意)、アニメーションファイルの精度を抑えるに関する2つのブログがありました。サンプリングに関するコードが含まれています、ぜひ読んでください。
アセット管理
Q4:異なるプロジェクトのAssetBundleは互いに読み取ることができますか? 暗号化の方法はありますか? Unityの異なるバージョンとAssetBundleの互換性はどの程度ですか?
1)Unityのバージョンが対応している限り、AssetBundleはお互いを読み取ることができます。
2)暗号化は一般的なファイル暗号化と同じですが、LoadFromMemoryを介してロードする必要があります。インターネットには多く の方法がありますが、 Googleしてください。
3)Unity 5.xバージョンでは、プロジェクトの上位バージョンは、AssetBundleの下位バージョンの読み取りをサポートしています。AssetBundleでTypeTreeを開く限り(デフォルトは開っている)、理論上問題はありません。
アセット管理
Q5:アセットをロードするためのCreatefromMemory APIに関して、UWAは効率が低いため推奨されていません。dllを更新する必要がありますから、このロード方式を使用していますが、何か他の方法ありませんか?誰もがこのAPIを使用していませんか? または使用できる特別な状況はありますか? このAPIの時間消費を最小限に抑える方法は? 提案をお願いします、ありがとう!
このAPIがコードのホットアップデートにのみ使用されば、確かにそれを使用する必要があります、納得できます。しなければLuaのような方式を使ってコードのホットアップデートをやります。
UWAは推奨していませんのは、Create / LoadFromMemory APIをリソースのロードに頻繁に使用することです。重要な構成ファイルまたはDLLコードの場合は、このAPIを使用してロードできます。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com