Загрузка / выгрузка огромного мира в Unity webgl с использованием пакетов ресурсов

#c# #unity3d #unity-webgl #assetbundle

#c# #unity3d #unity-webgl #assetbundle

Вопрос:

У меня очень большой 3D-мир / среда, который

  1. Я разделил на плитки размером 1000 x 1000 м (1 км).

  2. Создал пакеты активов из всех плиток, и в настоящее время существует только 5 пакетов активов (ожидается значительный рост, возможно, около 1000 пакетов активов).

  3. Загружая выгружая эти пакеты ресурсов, я вычисляю расстояние моего плеера / камеры до плитки, а затем вызываю загрузку или выгрузку.

Пожалуйста, смотрите ниже простой скрипт (загрузка фрагментов на основе расстояния):

         public Vector3 tileSize;
        public int maxDistance;
        public MultipleAssetBundleLoader[] tiles;

        void Start()
        {
            this.tiles  = FindObjectsOfType<MultipleAssetBundleLoader>();
        }

        void DeactivateDistantTiles()
        {
                foreach (MultipleAssetBundleLoader tile in tiles)
                {
                    Vector3 tilePosition = tile.gameObject.transform.position   (tileSize / 2f);

                    float xDistance = Mathf.Abs(tilePosition.x - playerPosition.x);
                    float zDistance = Mathf.Abs(tilePosition.z - playerPosition.z);

                    if (xDistance   zDistance > maxDistance)
                    {
                        tile.DestroyBundleObject();
                        //tile.SetActive(false);
                    }
                    else
                    {
                        tile.StartDownloadingAB();
                    }
                }  
        }

        void Update()
        {
            DeactivateDistantTiles();
        }
  

Функция StartDownloadingAB просто загружает пакеты ресурсов и создает экземпляр игрового объекта с сервера или кэша, в то время как DestroyBundleObject деактивирует игровой объект загруженного пакета (если он доступен).
Вот фрагмент кода для загрузки пакета ресурсов:

 public void StartDownloadingAB()
        {
            if (BundleLoadStatus == BundleLoadStatusEnum.bundleNotLoadedYet)
            {
                BundleLoadStatus = BundleLoadStatusEnum.bundlesLoading;
                downloadABRef = StartCoroutine(DownloadAB());
            }
            else if (bundleObjectsDeactivated == true amp;amp; BundleLoadStatus == BundleLoadStatusEnum.bundlesHasLoaded)
            {
                BundleObjectActive(true);
            }
        }

public IEnumerator DownloadAB()
{

    if (isBundleLoading == true)
        yield return false;

    BundleLoadStatus = BundleLoadStatusEnum.bundlesLoading;
    isBundleLoading = true;

    //Debug.Log("loading "   url);
    www = UnityWebRequestAssetBundle.GetAssetBundle(url);
    yield return www.SendWebRequest();

    if (www.error != null)
    {
        Debug.LogError("assetBundleURL : "   url);
        Debug.LogError("www error : "   www.error);
        www.Dispose();
        www = null;
        yield break;
    }

    AssetBundle bundle = ((DownloadHandlerAssetBundle)www.downloadHandler).assetBundle;

    GameObject bundlePrefab = null;
    bundlePrefab = (GameObject)bundle.LoadAsset(bundle.name);
    AssetBundleRequest bundlePrefabAsync = bundle.LoadAssetAsync(bundle.name, typeof(GameObject));
    yield return bundlePrefab;

    if (bundlePrefab != null)
    {

        //assetBundleToLoadObj = (GameObject)Instantiate(bundlePrefab);
        assetBundleToLoadObj = Instantiate(bundlePrefabAsync.asset as GameObject);
        assetBundleToLoadObj.transform.parent = envParent.transform;

        floorL7MeshRenderer.enabled = false;
    }

    www.Dispose();
    www = null;

    // try to cleanup memory
    Resources.UnloadUnusedAssets();
    bundle.Unload(false);
    //bundle = null;

    isBundleLoading = false;
    BundleLoadStatus = BundleLoadStatusEnum.bundlesHasLoaded;
}
  

И для уничтожения (фактически деактивации объекта, поскольку уничтожение является дорогостоящей функцией) пакета.

Проблема с производительностью:

Код работает нормально, и я могу загружать / выгружать пакет активов, но проблемы

  1. его производительность невелика (ожидается, что цикл будет расти).

  2. Webgl иногда бывает длинноногим, и он не является бесшовным или плавно работает.

  3. При загрузке контента (пакетов ресурсов) игра зависает, несмотря на использование LoadAssetAsync.

Кто-нибудь может помочь мне написать более эффективный и элегантный способ загрузки / выгрузки пакета активов?

Комментарии:

1. @ MuhammadFaizanKhan , я в замешательстве, почему не использовать GetAssetBundle ? или что-то в этом роде docs.unity3d.com/540/Documentation/Manual/… Я только что взглянул на ваш вопрос… извините..

2. @Fattie На самом деле я использую GetAssetBundle. Я обновил фактический код загрузки пакета активов, который отсутствовал ранее.

3. Чем этот вопрос отличается от того, который вы задавали на GameDev ?

4. Если вы внимательно изучите, есть разница между фрагментом кода, о котором я упоминал.

Ответ №1:

Вы должны прежде всего взглянуть на профилировщик и увидеть точки блокировки вашего приложения, если оно застряло при загрузке пакетов (сопрограммы все еще выполняются в основном потоке и могут вызывать задержки), возможно, вы захотите использовать асинхронную загрузку. Но вы хотели бы вызвать их заранее (когда игрок находится РЯДОМ с другим фрагментом, чтобы он был готов, когда он действительно достигнет фрагмента). Если ваши узкие места находятся где-то в другом месте, например, что-то связанное с рендерингом, вы можете подойти к этому другим способом (меньшие ресурсы с меньшим количеством вершин / треугольников или более агрессивный отбор). В любом случае для лучшего суждения вам нужно определить, в чем проблема, но с первого взгляда кажется, что проблема заключается в загрузке ресурса в основной поток.

Комментарии:

1. На самом деле Unity webgl является однопоточным, поскольку javascript является однопоточным. И я пытаюсь найти его через профилировщик, но моя сборка разработки выполняется успешно. я обнаружил, что мой проигрыватель отлично работает в редакторе.

2. Вам не нужна реальная многопоточность для выполнения асинхронных операций, вы можете просто начать загрузку ресурсов заранее, чтобы избежать проблем.

3. что вы подразумеваете под заранее? Я должен загрузить пакет ресурсов в зависимости от близости камеры / плеера.

4. @MuhammadFaizanKhan 1) Даже если сборка разработки прошла успешно, профилировщик все равно покажет ценную информацию, потому что в большинстве случаев потребление ресурсов (CPU, RAM и т.д.) Пропорционально в разных системах (в основном). Итак, вы действительно должны убедиться, что загрузка активов является проблемой (является ли это основной причиной потребления).

5. 2) Если проблема действительно в ресурсах и скорость проигрывателя настолько велика, что вам не удается загружать ресурс понемногу заранее (например, когда проигрыватель прошел половину блока, или меньше, или больше, в зависимости от размера вашего пакета), в этом случае вы можете либо сжать пакет, чтобы в нем было меньше байтов для передачи через жесткий диск или Интернет, либо вы можете попробовать поиграть с размерами блоков и ограничить скорость проигрывателя.