Unityアセット管理に関して、あう可能性のある問題——アセット管理編(その弐)

この記事は、前回の記事[「Unityアセット管理に関して、あう可能性のある問題——アセット管理編(その壱)」](https://qiita.com/UWATechnology/items/7768579ac3453f9254eb)続きであります。
前回の文章をまだ読まれていない皆様は、是非移動してご覧ください。

---
###メッシュ相関
**Q1: 一つのモデルがSkinned Mesh Rendererのインスタンスに対応している場合、キャラクターが増加するとメモリも増えますか？**
>単純に一つのキャラクターPrefabから複数のインスタンスをインスタンス化するだけで、Meshは複数表示されません（これは、Texture、AnimationClip、Materialなどの他のアセットと同じです）。メモリ内に複数のコピーが見つかった場合は、プロジェクト内のAssetBundleのロード方法から検討できます。これは、同じAssetBundle内の同じキャラクターのPrefabであっても、「load-instantiate-unload」操作を繰り返すと、依然として複数のMeshを導きます（当然、他のアセットも同じです）。

---
**Q2：2つのFBXに依存する一つの特殊効果があります。 これら2つのFBXのチェックを削除すると、エディターは正常に運行しました。 そうしないとクラッシュしますが、その理由は何ですか？**
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b15e1f65-90a0-892d-ae8e-c3d34c68ade1.png)
>FBXのRead/Write Enabledをオフにすると、メッシュのコピーがメモリに保存されなくなるため（ビデオメモリのみ）、そのプロパティにアクセスおよび変更することはできなくなります。パーティクルシステムは、通常、粒子の頂点属性を動的に変更する必要があります。したがって、理論的には、パーティクルシステムに提供して使用されたMeshは、Read/Write Enabledをオンにする必要があります。EditorでMeshとTextureは強制的にオンになるため、実機で問題が発生します。

---
**Q3：MeshBakerでベイクしたMeshはPrefabに保存できますが、FBXのようにモデルインポート設定でGenerate Lightmap UVs や他の情報を設定することはできません。この状況に対処する方法はありますか？**
>まず、理論的には、メッシュは1つのLightmapIndexプロパティとLightmapOffsetプロパティのみを持つことができます。これにより、Meshの合併はLightmapをベイクする前に行う必要があります。
Meshの合併を行うとき、注意すべきなのは、一つのMeshに対して、UV2のすべての三角面のUV領域が互いに重なってはならないため、単純に直接合併することはできません。
合併する前のそれぞれのMeshのUV2領域は、[0,1] x [0,1]の間隔にあるはずだと考えると、合併する時に各UV2領域を縮小およびシフトできます。互いに重ならない状況で同じ間隔[0,1] x [0,1]に配置されます。
UV2も正しく結合された後、Lightmapを再ベイクしたら正しい効果を取得ですます。

---
###アニメーション相関
**Q1：エディターでAnimator Controllerファイルの各StateにあるAnimation Clipをバッチ置換したいのですが、UnityがこのようなAPIを提供していないようです。Runtimeどきのみに替えられます。**
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/f7ac6100-0389-6e8a-5a03-d08dc74a6587.png)
**現在は、エディターでAnimator Controllerをバッチ置替えるプラグインを作成したいです。Unityエンジンはこのようなインターフェイスを提供していますか？**
>Unity 5.xには、Animatorの要素にアクセス、変更、作成できる完全で安定したAPIがすでに存在します。名前スペースはUnityEditor.Animations にあります。下記のBlogにスクリプトを使用してAnimatorControllerとそのさまざまなプロパティおよびパラメーターを作成する簡単な例があります。

>https://blogs.unity3d.com/2014/06/26/shiny-new-animation-features-in-unity-5-0/

>さらに、Animation Clipを変更したい場合、一つのAnimationState の Motionプロパティを直接変更できます。Animation ClipはMotionから継承できます。

---
**Q2：AnimatorがFBXアニメーションファイルをコピーして引用するのではなく、FBX内のアニメーションファイルを直接引用する場合、すべてのFBXはパッケージ化中にパッケージ化されますか？**
> この場合、FBXはAssetBundleに含まれません。 アニメーションファイルのコピーすることは、一般的にアニメーションファイルを依存パッケージとして直接パッケージするためです。

---
**Q3：私たちのアニメーションはFBXファイルに配置されています。**
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/03c10639-2579-de6b-dd74-3658738b5315.png)
**その結果、パッケージ化時にいくつかの役に立たないファイルもAssetBundleパッケージに入れられました。実際、最後のいくつかのアニメーションファイルのみを使用したいです。ただし、エディターでFBXファイルのアニメーションファイルを選択するときに、AssetLabelsウィンドウが表示されず、AssetBundle Nameを設定できません。**
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/c2cbbac9-89c6-e9d9-68be-2e0b32542f42.png)
**これはコードでのみ設定できますか？ それとも、次の図のように、AssetBundleNameを設定できず、アニメーションファイルしか抽出できないということですか？**
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/e7383854-4343-1506-6a63-085b1eef6dec.png)
>Unity 5.xのパッケージ化規則では、FBXのMeshまたはAnimationClipの単一アセットのAssetBundle名を手動で設定することは確かにできないです。したがって、この部分のアセットを抽出して依存パッケージとしてしたい場合、今には最初にスクリプトでこの部分のアセットを抽出する必要があります。

---
###マテリアル相関
**Q1: マテリアルインスタンスの数が特に多いことがわかりました。これがパフォーマンスにどのように影響するかをお聞きしたいのですが、何か提案はありますか？**
>Materialのメモリ占用は一般に非常に小さいため、多数のMaterialアセットからのメモリへのプレッシャーは実際には非常に小さいです。 ただし、シーンの切り替え時間に影響します。つまり、冗長が多いほど、シーンを切り替えるときのUnloadUnusedAssetsのコストが大きくなり、シーンの切り替え時間が長くなり、DrawCallの合併にも影響します。したがって、開発チームに、アセット冗長性の原因を可能な限り特定し、それを修復および改善することをお勧めします。 これについては、UWAパフォーマンスレポートの「分析と推奨」で詳しく説明されています。
![18-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/166fe0d3-42d2-7cca-9624-3c4a26e9166e.png)

---
**Q2：UWAでパフォーマンステストを実行しましたが、アセットメモリの詳細は次のとおりです。この数値のピーク値が1より大きい場合、問題が発生しますか？ただし、一つのMaterialは複数のコピーでインスタンス化される可能性があります。インスタンス化された後、オブジェクトが解放され、元のMaterialがクリーンアップされます。これで、冗長ではなくなりませんか？ほとんどのMaterialが操作し、テクスチャアセットに影響ありませんはずです。そして、確かに同じ画面に多くのキャラクターがいる状況があればどうしたらいいですか？**
![19-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/0dbae652-7f86-efab-5243-d97ea0a2eea1.png)
>そうです、ピーク数が1より大きい場合は、アセット冗長性のリスクがあることを意味します。Materialインスタンス化の場合、後者には「instance」サフィックスが付いており、識別が容易です。new Material(Shader)で取得する場合、「instance」サフィックスはありません。ただし、どちらの方法を使用しても、ピーク数が多い場合は注意が必要です。 一般に、他の方法を使用して、冗長性を可能な限り回避できます。

>変更がランダムではなく、Materialパラメータの変更が比較的小さい場合、パラメータの変更に応じてMaterialをいくつだけ作成し、スクリプトを介して動的に割り当てられます。 ランダムまたはアニメーションの場合は、何も方法はありません。需要を変更するか、多くを受け入れます。

---
###Shader相関
**Q1：内蔵のシェーダーを使用してシーンをレンダリングしましたが、深度マップにコンテンツがあります。 しかし、自分のShaderを使用すると、取得した深度マップは1にすぎません。原因は何ですか。 ZWriteはもう開きました。**
>Unity 4.xで、Unityの_CameraDepthTextureの生成は、Rendering Pathの選択及びデバイスの違いによって異なる方法を採用して実現します。一つはdepth bufferから直接取得することで、もう一つはPassを追加してテクスチャにレンダリングすることであります。現在、モバイルデバイスでは主に後者で実現します。後者の実現にはShader Replacementの規則を借りました（バッチで簡単なShaderに置換し、テクスチャに深くレンダリングします）。

>したがって、カスタムShaderを使用する時に、RenderTypeのTagを正しく設置し（すべての内蔵Shaderは設置済み）、Shader Replacementを正確に実行させます。具体的にこのドキュメントを参照してください。http://docs.unity3d.com/Manual/SL-ShaderReplacement.html

>Unity5.xでは、Shader Replacementによって実現されなくなったため、深度値を書き込むには、”LightMode”= “ShadowCaster”のTagsを含むPassを追加する必要があります。

---
**Q2：私たちのゲームはSpineプラグインを使用しています。クロップアニメーションを使用する必要があるため、Shaderを変更しましたが、使用すると異常が発生します。 Shader wants normals, but the mesh Skeleton Mesh doesn’t have them.原因は何でしょうか。**

>開発チームに注意すべきなのは、Surface Shaderがコード生成中にデフォルトでnormalを処理しますが（つまり、Spine/Skeletonは実際にnormalが必要です）、対応するMeshにはnormalはありません。だから、プレビューウィンドウでレンダリングする時に、Meshにnormal情報が含まれているかどうかを確認し、ないならこのエラーを報告します。

>開発チームは、Vertex＆Fragment Shaderを作成してnormalの処理を回避するか、normalのあるMeshを作成してこの問題を回避することができます。

---
**Q3：以前、PCゲームの時代で、異なるPass又は異なる照明環境に応じて、マテリアルをオフラインでShaderCacheに事前にコンパイルできます。実行時にマテリアルを合併してリアルタイムでコンパイルする必要はありません、バイナリコードをロードするだけで良いです。Unityはこのようなことを行いましたか？ プラットフォームと環境が事前にコンパイルされたShaderに応じています。**
>Binary Shaderのローディングをサポートするデバイスの場合、Shaderが初めてコンパイルされるときに、対応するBinary Shader Cacheが生成されます。生成されたBinaryファイルは、Application.persistantPathの横のCacheディレクトリにあります。

---
**Q4：同じ効果を前提として、パフォーマンスから見ると、V＆FまたはSurface for Shaderを使用する方が良いですか？**
>Surfaceによって生成されるV＆Fは複雑で、多くのブランチがあります。＃pragma surfaceパラメータの選択に注意を払わないと、不要なコストが発生する可能性があります。たとえば、Unity5.xでデフォルトで作成されたSurfaceShaderを直接使用する場合（デフォルトのパラメータは#pragma surface surf Standard fullforwardshadowsです）、ShaderはPhysically based Standard Lightingを実行します。これはモバイル側で非常に高価で、必要はありません。

---
**Q5：あるShaderにCulling Offが設定されている場合、後続のすべてのShaderのレンダリング状態に影響しますか（Cullingが後で設定されないと仮定します）？**
![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/1ad870fb-2549-6765-4238-cf3644558dc7.png)
>1回のDraw Callが提出する関するRender Stateは、次のレンダリング状態には影響しません。 指定CullモードがShaderに表示されていない場合は、デフォルトのCull Backを使用します。

---
**Q6：ShaderをResourceディレクトリに配置し、エディターのGraphicSettingにも追加しました。また、空のPrefabをロードするときに、対応するShaderをバインドすることも試しました。ただし、このShaderはEditorに正常に表示できません。実行時にポインターがあるので、もう一度ポイントすることで表示できますが、パッケージ化後、携帯で正常に表示されます。**
>これは確かにUnityの既知の問題です。パッケージ化された後、AndroidおよびiOSの一部のShaderはEditorで正常に表示できません。主な原因は、パッケージ化するときに、対応するプラットフォームのShaderプリコンパイルされたコード（glesなど）のみがパッケージに入力されるため、Editorで実行が失敗するためです（通常、Editorはd3dドライバー）。したがって、この問題を回避するには、EditorでShaderを再指定することしかできません。

---
**Q7：Shaderでこのようにコードを書きました。**

```ruby:
o.texcoord1 = vec2(mod(v.texcoord.x,1.0),mod(v.texcoord.y,1.0));
```
**しかし、エラー「undeclared identifier ‘mod’ at line 106 (on d3d11)」が出できました。この原因は何ですか？**
>このエラーは、modがd3d11 Shaderで定義されていないことを示しています。開発チームがd3d11プラットフォームでShaderを使用する必要がない場合は、次のプリコンパイル済みの指令を追加できます。
``#pragma exclude_renderers d3d11``
それでもShaderが正常に表示されない場合は、EditorでDX11が使用されていることが原因である可能性があります（タイトルバーから確認できます）。DX9のパラメーターを変更してみることができます：Build Settings → PC, Mac …をクリックし → Player Settings（Switch Platformはいらない）→ Auto Graphics API for Windows のチェックを外し、Direct3D9のみを保持します。

---
###フォント相関
**Q1: 美しさのために、2セットのフォントを同時に使用する必要があります。 ただし、フォントのセットが追加されるたびに、メモリは約50MB増加しました。 他のUnityゲームを最適化するときに、同様の状況にどのように対処しましたか？**
>装飾文字が少ない場合、特定の装飾文字の表示効果を実現ために、単一のフォントテクスチャで実現できます。装飾文字の多い場合、別セットのフォントを使用することのみが推薦できます。一般的に、フォントのメモリを10MB以内に制御することは完全にできます。
開発者は、Unity Profilerでフォントメモリ使用量を直接確認できます。

---
**Q2：このフォントは私が作成したプリセットで使用されています。**
![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/6539c961-af68-768e-4411-9768fc292ce5.png)
**このプリセットをAssetBundleパッケージ化し、Profilerで分析したところ、メモリが2つあることがわかりました。**
![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/c020846f-3281-9532-11cd-cf6f6581ad8c.png)
**下方のSIMHEIはTTFのものはずですが、なぜメモリが2つありますか？**
>通常、TTFファイルには、通常のフォント、太字のフォント、斜体などの複数のフォントが含まれます。Unityでは、異なるフォントアセットに分割され、それらは相互に依存します。したがって、プロジェクトで太字が必要な場合は、2つのフォントがメモリに表示されるのが正常ですが、太字にする必要がない場合は、太字のフォントを含まないフォントファイルを見つけてTTFを置き換えることができます。

---
###パーティクルシステム
**Q1：UWAでアセットチェックを提出しました、アセットは依存関係パッケージです。レポートには、Default-Particleアセットに多くの冗長性があることが示されていますが、これは正常ですか？**
![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/cf8bf633-cf17-9be2-56d9-dba6f5b7b55b.png)
>Default—Particleとは、パーティクルシステムのデフォルトのアセットです。デフォルトのパーティクルシステムが使用され、マテリアルが変更されておらず、各パーティクルシステムが一つのAssetBundleファイルにする場合、冗長性の問題は正常です。

>これに対して、開発チームに下図にあるデフォルトのマテリアルを自分のアセットに置き換え、Built-inアセットを使用しないことをお勧めします。これで、依存パッケージに介して、このアセットの冗長性問題が発生しません。
![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/73f066f2-3b09-658b-7cf4-4dd27d3191e4.png)

---
###Lightmap
**Q1：Unity 5.xは指定されたMeshを一枚のLightmapにベイクさせられますか？**
>Unity5.xにはそのような機能アイテムはありません。私たちの判断によると、このような要件は通常動的ローディングのためです。したがって、Meshをさまざまなシーンにグループ化して別々にベイクすることができます。 ロードする時に、Lightmapはスクリプトを介して動的に合併でき、オブジェクトのLightmap Indexを正しくオフセットすれば解決できます。

---
**Q2：Baked GIでLightmapの待機時間は比較的長いです（Realtime GIはもうオフにしました）。待機時間を短縮できる推奨パラメーターまたは方法はありますか？**
>私たちが知る限り、Unity 5.xでは、ベーキング時間に影響を与える主な原因は、面積の広い領域がLight Transport過程を延長することです（Enlightenの規則だから）。面積の広い領域を分割してベイク速度を上げることができます（通常、面積の広い領域を分割するとレンダリングパフォーマンスも向上します）。

>主な原因は[こちら](http://forum.unity3d.com/threads/light-transport-problem-with-large-objects.310405)を参照できます。

---
**Q3：下図のように、1枚目はLightmapをベイクしないシーン効果、内部に点光源があり、2枚目はLightmapをベイクした後のシーン効果です。 同じ点光源の効果がなぜそんなに違うのか聞いてもいいですか？**
![26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/509cb651-1da9-08ba-ec8a-8b7d7d43230d.png)
![27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/3a0ba62d-de7e-fdbc-1405-7a86fbfc5dc9.png)
>簡単に言えば、これがリアルタイムの直接照明とグローバル照明の違いです。
前者はレンダリング中に光源と単一のオブジェクト間の直接照明のみを処理できますが、後者は光線追跡や放射輝度などの複雑なアルゴリズムを使用して、すべてのオブジェクトのすべての表面間で反射される光情報を計算します。これもLightmapのベーキングに時間がかかる理由です。グローバル照明の場合、他の表面の反射のために物体の裏側でさえも照らされ、直射日光の下ではそのような効果を達成できないことがわかります。

---
**Q4：プリレンダリングGIをオフにすると、IndirectResolutionが表示されますが、このパラメータの用途は何ですか？パラメータを大きくするとレンダリング時間が大幅に長くなるのに、ベイク効果かないのはなぜですか？**
>簡単に言えば、この値は主にGIのベーキング密度を制御します。値が大きいほど、各単位距離内のTexelが多くなります。つまり、ベーキングが繊細になるほど、当然、ベーキング時間が長くなります。 値をできるだけ大きくする必要はありません。シーンが小さいほど、値を小さくすることをお勧めします。 値が1の場合、ほとんどのシーンでベーキング効果は十分ですが、値を大きくしても効果は大きく向上しません。

>開発者は、[Unityの公式ドキュメント](http://docs.unity3d.com/Manual/GlobalIllumination.html)を参照することもできます。

---
**Q5：Lightmapが失いました。Unity 5.1.2のAssetBundleを使用してホットアップデートを実行しました。アセットをエクスポートする時に、すべての依存関係リストファイルのエクスポートを分析しました。例えば、シーンをエクスポートすると、シーンのベイクされたLightmapSnapshot.assetファイルがエクスポートできないため、実行時にシーンのLightmapが失われます。**
>LightmapSnapshot.asset自体はEditorで使用され、AssetBundleに個別にパッケージ化することはできません。実行時にLightmapをロードする1つの方法は、シーン（.unityファイル）をAssetBundleとしてロードすることです。ライトマップ情報は、シーンAssetBundleに入力されます（ライトマップ情報はシーンにバインドされているため）。もう1つは、Lightmapsettings.Lightmaps方法を使用して実行時に設定を行うことです。注意すべきなのは、Lightmap情報はUnity5.xのPrefabに保存されないため、同時にPrefabのLightmap情報（LightmapindexとLightmapscaleoffset）を再設定する必要もあります。

>まだ一つの可能性があります。Unity 5.xにShader Stripping機能が追加され、パッケージする時に、デフォルトで、現在のシーンのLightmapとFogの設定に従って、アセット内のShaderからコード削除を行います。これは、空きシーンでパッケージすると、Bundle内のShaderがLightmapとFogのサポートを失い、実行時にLightmapとFogが失われることを意味します。Edit→Project Settings→GaphicsのShader StrippingにあるmodesをManualに変更し、対応するModeをチェックすることでこの問題を回避できます。

---
**Q6：LightmapはPCで正常に表示されますが、Androidプラットフォームでは色の違いがあります。通常、色はより暗いです。**
>一般的に、色と明るさの違いを引き起こす可能性のある2つの状況があります。

>1、UnityがベイクしたLightmapは32bitのHDRマップであり、モバイルデバイスは通常HDRマップ（32bit per channel）をサポートせず、LDRマップ（8bit per channel）の形式で処理されるため、色違いの問題が発生する可能性があります。

>ですから、UWAからのアドバイスは：
1）モバイルプラットフォームでMobile/Diffuseマテリアルを使用すること、Standard Assets(Mobile) packageをロードして取得できます。より適切な効果を得たい場合は、LightmapのDecodeLightmap関数を自分で変更する必要があります。この関数はUnity/Editor/Data/CGIncludes/UnityCG.cgincファイルにあります。この方法を採用しても、PCと全く同じ効果は得られないことに注意してください。
2）PCとモバイルプラットフォームの表示効果を同じにする必要がある場合は、画像編集ソフトを使用して、LightmapをPNG（8bit per channel）などのLDR形式に変更できます。
3）このような問題を回避するために、ベーキングに強すぎるLightを使用しないでください。Lightの強度（Intensity）が高いほど、色違いが深刻になります。シャドウロスがある場合は、モデルのLightmapindex、Lightmapscaleoffset、UV2など、Lightmapサンプリングに影響を与えるいくつかのパラメーターを確認してみてください。

>2、もう1つの可能性は、露出オーバーがあることです。playersettings →use direct3d 11をオフして、問題は解決されるかどうかを確認してください。

---
**Q7：同じシーンでベイクされたLightmapには、5.3MBのサイズの1024×1024ライトマップを2枚使用しましたが、他の人は4.3MBのサイズの1024×1024イメージを3枚使用しました。 このライトマップのサイズに影響するものと、それを調整する場所を尋ねてもよいですか？**
>まず、ライトマップのタイプを確認してください。Singleタイプは1枚だけを生成し、DualとDirectionalは2枚を生成します。次に、現在のリリースプラットフォームを確認してください。AndroidでのLightmapはStandaloneよりも小さくなります。 プラットフォームが異なれば、使用される圧縮形式も異なります。さらに、LightmappingにあるLock Atlas、Resolution、Paddingなどのオプションも、最終的にベイクされたライトマップのサイズに影響します。

---
**Q8：ゲーム内で、あるMeshは編集する時にLightmapを受信しますが、何らかの理由で同じMesh（マテリアルも同じ）を合併しました。ただし、元のLightmapが合併されたMeshに影響を与えなくなったことがわかりました。合併されたMeshに元のLightmapを受信させるにはどうすればよいですか？**
>複数のLightmapがある場合、合併されたMeshのベイク情報は異なるLightmapに表示される可能性があるため、Meshを手動で合併すると問題が発生します。ただし、合併されたMeshはレンダリング時に1つのLightmapしか使用できないため、uv2がLightmap情報を読み取るときに問題が発生し、この現象を引き起こします。

>実際、同じマテリアルを持つStaticオブジェクトの場合、UnityのStatic Batchingが自動的に完了するため、手動でMeshをcombineする必要はありません。ある特定の要件で必ずMeshを合併する必要がある場合、Meshに必要なLightmapも合併する必要があり、対応するuv2を変更する必要があります。それだけでなく、ShaderのLightmapもそれに応じて変更する必要があり、これはより複雑です。したがって、開発チームの開発に多くの時間がかかる可能性があるため、このアプローチはお勧めしません。

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

UWA公式サイト：https://jp.uwa4d.com
UWA公式ブログ：https://blog.jp.uwa4d.com