Addressableホットアップデートアセットタイプに関する質問

今回の話題：
1）Addressableホットアップデートアセットタイプに関する質問
2）Unityの再起動後、ローカルで削除されたFBXのDefaultMaterialが無効になります
3）MeshRendererの効果を実現する方法
4）Itemを動的にロードするUGUIのDrawCall問題
5）Loading.CheckConsistency[Editor Only]エディターでの最適化問題

---

 
Addressable
---

**Q：Addressableは、AssetBundleを使用せずに、Text/Xmlなどの元のアセットをホットアップデートできますか？Addressableをホットアップデートソリューションとして使用するかどうかを検討しています。一部のアセットは、AssetBundleを経由せず、直接読み取りたいと考えています。Addressableがそれをサポートしているかどうかわかりませんか？**
 
>A：現在、AddressableはRawアセットを直接サポートしておらず、基本的にAssetBundleを使用しています。
1つのアイデアは、これらのアセットを単独にAssetBundleにすることです。論理的には、それは引き続き別個のTextとして扱われ、Binaryはそれを読み取ります。
もう一つのアイデアは、拡張変換を行うことです。「File.ReadAllText(path);」を使用して内部でアセットを取得するTextDataProviderというProviderクラスがあることがわかります。実際にAddressableAssetGroupSchemaとBuildScriptBaseを実装し、RawAssetSchemaとBuildScriptRawAssetの拡張を作成してパッケージ化することができます。

----



Editor 
---

**Q：Addressablesシステムを使用してAssetBundleにすると、Default Materialが冗長になります。パッケージャーでスクリプトを実行してFBXのデフォルトのマテリアルを削除し、紫色に変えますが、プロジェクトを再び開いた後、デフォルトのマテリアルが元に戻り、冗長も残られています。（Unityバージョン2018.4.34）**
 
マテリアルが削除されたスクリプト：
```
using System;
using UnityEngine;
using System.Collections;
using System.IO;
using UnityEditor;
using Object = UnityEngine.Object;

class DisableMaterialImport : AssetPostprocessor {

    [MenuItem("Tools/Reimport All Model")]
    public static void ReimportAllModel()
    {
        var assetPaths = AssetDatabase.GetAllAssetPaths();
        Array.Sort(assetPaths);
        Debug.LogWarning(string.Format("Total assets count: {0}", assetPaths.Length));
        int processedCount = 0;

        foreach (string assetPath in assetPaths)
        {
            string normalizedAssetPath = assetPath.ToLower();
            if (!normalizedAssetPath.EndsWith(".fbx") &&
                !normalizedAssetPath.EndsWith(".obj") &&
                !normalizedAssetPath.EndsWith(".3ds"))
            {
                continue;
            }

            var modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
            if (modelImporter == null || modelImporter.importMaterials)
            {
                continue;
            }


            AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ImportRecursive | ImportAssetOptions.ForceUpdate);
            Debug.Log(assetPath, AssetDatabase.LoadMainAssetAtPath(assetPath));

            processedCount++;
        }

        Debug.LogWarning(string.Format("Total processed model count: {0}", processedCount));
        AssetDatabase.SaveAssets();
    }

    private static void FixedModelImport(ModelImporter modelImporter, GameObject model)
    {
        //Debug.Log("FixedModelImport "+model);
        var renderers = model.GetComponentsInChildren<Renderer>(true);
        if (renderers == null)
        {
            return;
        }

        modelImporter.importMaterials = false;
        modelImporter.importBlendShapes = false;
        modelImporter.importAnimation = false;
        modelImporter.isReadable = false;
        modelImporter.optimizeMesh = true;

        foreach (var renderer in renderers)
        {
            if (renderer == null)
            {
                continue;
            }

            renderer.sharedMaterials = new Material[renderer.sharedMaterials.Length];
        }

/*        var animator = model.GetComponent<Animator>();
        if(animator!=null) Object.DestroyImmediate(animator);*/
    }
```
>A1：ここでテストしましたが、元に戻りません。プロジェクトがロールバックされた可能性があります。さらに、このスクリプトのReimportAllModelは、「Import Materials」がオンになっていないモデルにのみ有効です。


>A2：何度か試したところ、Cache Serverをオフにすれば済みます。
---

Editor
---

**Q：Prefabをシーンに直接ドラッグするのではなく、レベル内のオブジェクトをスクリプトを介して間接的に作成する必要があります。 そのため、MeshRendererのようにシーンに直接表示して、選択と編集を簡単にできるようにする必要があります。**
 
コードは次のようになります。
```
public class SpawnPrefab : MonoBehaviour
    {
        public GameObject prefabEntity;
        void Start()
        {
            Instantiate(prefabEntity);
        }
    }
```
MeshRendererには、次の2つの特性があります。
1.Editorモードでは、SceneViewとGameViewが同時に表示されます。
2.実行モードで描画されたオブジェクトは、SceneViewで選択できます。
 
現在、OnDrawGizmos関数でGraphics.DrawMeshを呼び出していますが、SceneViewで選択できず、Updateで描画するときにエディターモードで表示できません。方向が間違っているような気がします。
>A：OnDrawGizmosで描画することは明らかに不可能です。この関数は、Runtimeがパッケージ化されているまでは呼び出されません。
SceneViewで「編集」を選択するには、シーン内のGameObjectである必要があります。 （GameObjectでMeshRendererをハングアップすると、「SceneView編集」と「ランタイムスクリプト作成」の2つの要件を確実に満たすことができます。）
問題主の要件がGraphicsの即時レンダリングインターフェイスを使用してMeshを描画し、それをEditorの下に表示することである場合は、クラスにExecuteAlwaysプロパティを追加したら済むが、SceneViewで選択して編集することはできません。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/3bf41f07-92ab-dfe0-5de8-66a039ecbc25.png)
Sceneウィンドウに編集機能も実装したい場合は、シーンにあるMeshRendererをハングアップしたGameObjectを描画するMeshにバインドすることしかできません。 Graphics.DrawMesh描画オブジェクトのTransformは、このGameObjectと一致すればいいです。次の図に示すように、GameウィンドウはOnPostRender関数でGraphics.DrawMeshNow関数によって描画される効果を示し、Sceneウィンドウはバインドされた編集と調整用のオブジェクトです。2つのTransformは同じです。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/47d5e3e3-59e8-79dd-9756-239a04725b84.png)
キーコードは次のとおりです。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/1097ec6b-6a9c-cce5-b9d5-f839b87bc401.png)

---

UGUI
---

**Q：Itemの動的ローディングに関して、これは散在していますか？Itemが多数ある場合、何回のDrawCallが必要になりますか？誤っていると感じたが、この面に関してどう勉強すれば良いか？または実際の作業では、これは気にしなくても良いでしょうか？**
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/83bb5eeb-3a47-9c8d-d2fe-f88cd8b058f2.png)
**これは所詮一つの例にすぎません。通常のプロジェクトで要素がこれほど少なくなることは不可能です。要素が多い場合、それらを動的にロードするにはどうバッチすればよいですか。動的にロードされたItemの要素を処理するための別のスクリプトを作成する場合、それはあまりにも不合理です。たとえば、NGUIはDepthを使用して、パネルの下の要素のバッチ処理を制御しました。UGUIが配置の順序によって制御されることだけを知っています。この場合、動的ロードにはあまりにも不向きです。**
 
>A1：同じマテリアルを共有し、他のバッチ条件を満たすメッシュは、隣接する順序でレンダリングすることでバッチ処理できるため、DrawCallが削減されます。したがって、バッチ処理の鍵は、同じマテリアルを使用するオブジェクトのレンダリング順序を制御することです。
バッチ条件と最適化方法には、次の資料があります。
https://blog.uwa4d.com/archives/optimzation_cpu.html
https://docs.unity3d.com/Manual/DrawCallBatching.html
Unityでのレンダリング順序に影響を与える要因：
https://zhuanlan.zhihu.com/p/55762351
さまざまなアセットがさまざまな方法でレンダリング順序を制御します。たとえば、MeshRendererはマテリアルのRenderQueueを設定でき、ParticleSystemはOrderInLayerとSortingLayerを設定できます。
UGUI要素のレンダリング順序は、UGUIプラグイン自体によって決定されます。必要なのは、UGUIが要素をレンダリングする方法を理解し、同じマテリアルのUI要素を可能な限り隣接する順序でレンダリングすることです。具体的な重要なポイントは次のとおりです。同じレイヤーのUI要素が重複しないように、UGUIはレイヤーごとに描画され、同じマテリアルのUIが同じレイヤーとして描画されるようにします。たとえば、問題主のプロジェクトのImgは同じレイヤーに配置され、Txtは2番目のレイヤーに配置されます。 2つのImgを積み重ねると、1つのImgが2番目のレイヤーとして描画され、Txtの描画とImgの描画が互いに散在し、DrawCallが追加されます。
それらが重ならない限り、同じアトラスを使用する同じレイヤーの要素は自動的に一緒にバッチ処理されます。複雑なインターフェイスの場合は、可能な限りアトラスを組み合わせることができます。複数のレイヤーの場合、同じマテリアルの要素が同じアトラスを使用し、隣接する順序で描画することを確認する限り、バッチ処理することができます。UGUIにアトラスが増えると、DrawCallを制御する良い方法はなくなります。
 
>A2：Itemの領域が別のItemの領域と重ならない限り、すべてのテキストは2番目のレイヤーとしてカウントされ、Imageは1番目のレイヤーとしてカウントされます（2回のDrawCallのみ）。他のUI要素の影響を受けることを恐れている場合は、これらのItemをCanvasだけに配置して、他のUI要素の影響を受けないようにすることができます。

---

 
Editor
---
**Q：Loading.CheckConsistency [Editor Only]はエディターで時間がかかりますが、それは何をし、どのように最適化するのですか？**
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/75f84c55-2d76-def8-cd78-228e27f2e008.png)
>A：ReadObjectは、ロード後にObjectを実際に逆シリアル化します。一つのPrefabが逆シリアル化されたら、多数のObjectが生産されます。IntegrateAllThreadedObjectsはこれらのObjectをトラバースし、Loading.CheckConsistencyはこれらのオブジェクトをトラバースするときにデータの整合性をチェックします。
いわゆる整合性チェックとは、たとえば、次の図のPrefabのシリアル化されたファイルの場合、2つの赤枠付けのfileIDが整合性があるかどうかをチェックすることを意味します。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/d2d0c665-16f9-4e01-62fa-ea417a55bd2e.png)

ソース：https://www.cnblogs.com/luguoshuai/p/12323186.html
このブログで述べたように、2つのfileIDに一貫性がない場合、CheckConsistencyエラーが発生します。
整合性チェックがEditorでのみ実行され、パッケージ化後にRuntimeをチェックする必要がないのはなぜですか？筆者は、パッケージ化中にすべてのオブジェクトがチェックされたと推測しているため、Runtimeをチェックする必要はなく、チェックによって生じる高い時間コストも回避できます。
証拠を次の図に示します。パッケージ化されたエラースタックには、CheckConsistencyのステップが含まれています。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/668fa26c-af0f-33cb-4323-74e4e9a86b86.png)

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

今なら、UWA GOTローカルツールが**15日間に無償試用**できます！！
よければ、ぜひ！

UWA公式サイト：https://jp.uwa4d.com
UWA GOT OnlineレポートDemo：https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ：https://blog.jp.uwa4d.com