2

I have a very large 3D world/Environment which

  1. I have divided into 1000 x 1000 m (1km) tiles.

  2. Made assetbundles of all tiles and currently there are only 5 asset bundles (Expected to grow significantly maybe around 1000 asset bundles).

  3. Loading\unloading these asset bundles, i am calculating the distance of my player/camera with the tile and then calling for load or unload.

Please see below simple script (Loading tiles based on distance):

        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();
        }

The function StartDownloadingAB simply download the asset bundles and instantiate game object from server or cache while DestroyBundleObject deactive the game object of the loaded bundle (if available). Here are code snippet for download the asset bundle:

public void StartDownloadingAB()
        {
            if (BundleLoadStatus == BundleLoadStatusEnum.bundleNotLoadedYet)
            {
                BundleLoadStatus = BundleLoadStatusEnum.bundlesLoading;
                downloadABRef = StartCoroutine(DownloadAB());
            }
            else if (bundleObjectsDeactivated == true && 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;
}

And For Destroying (actually deactivating the object as destroy is expensive function) the bundle.

Performance Issue:

The code is working fine and I am able to load/unload the asset bundle But the problems are

  1. its performance is not good (The loop is expected to grow).

  2. The Webgl is leggy sometime and it is not seamless or smoothly runs.

  3. When downloading the content(asset bundles) the game freeze despite using LoadAssetAsync.

Can anyone help me to write more efficient and elegant way to load/unload asset bundle ?

Muhammad Faizan Khan
  • 10,013
  • 18
  • 97
  • 186
  • @ MuhammadFaizanKhan , I'm confused, why not using GetAssetBundle ? or this sort of thing https://docs.unity3d.com/540/Documentation/Manual/DownloadingAssetBundles.html I have only just glanced at your question ... sorry .. – Fattie Apr 11 '19 at 11:10
  • @Fattie Actually I am using GetAssetBundle. I have updated the actual asset bundle loading code which was missing previously. – Muhammad Faizan Khan Apr 11 '19 at 11:26
  • How is this question different from [the one you asked on GameDev](https://gamedev.stackexchange.com/questions/169448/open-world-loading-unloading-with-asset-bundle-in-unity)? – Martijn Pieters Apr 14 '19 at 12:58
  • If you closely inspect, there is a difference between the coding snippet i have mentioned. – Muhammad Faizan Khan Apr 14 '19 at 16:46

1 Answers1

2

You should first of all look at the profiler and see the choke points of your application, if it is stuck at loading bundles (coroutines are still run on the main thread and might cause lags) you might want to use async loading. But you would want to call those ahead of time (when player is NEAR the other chunk, so its ready when he actually reaches the chunk). If your bottlenecks lies somewhere else, for example something to do with rendering you might approach it some other way (smaller assets with less vertices/triangles or more aggressive culling). Either way for better judgement you need to locate where the problem is, but from the first look it seems that loading the asset on main thread is the problem.

  • Actually unity webgl is single thread as javascript is single thread. And i try to locate it through profiler but my development build is executing successfully. i found that my player working fine in editor. – Muhammad Faizan Khan Apr 08 '19 at 14:18
  • You don't need real multithreading to run async operations, you can just start loading the assets beforehand to avoid choke points. – Arman Papikyan Apr 10 '19 at 13:59
  • what do you mean by beforehand? I have to download asset bundle according to the proximity of camera/player. – Muhammad Faizan Khan Apr 11 '19 at 05:25
  • @MuhammadFaizanKhan 1) Even if the development build is successful, the profiler will still show a valuable information, because in the most cases the resource consumption (CPU, RAM, etc) is proportional across the systems (mostly). So you should really make sure that the loading of the assets is the problem (is it the major consumption cause). – Arman Papikyan Apr 15 '19 at 12:37
  • 2) If the problem is indeed in the assets and the speed of the player is so great that you are not managing to download the asset little by little beforehand (when the player has made it half across the chunk for example, or less or more, depending on your bundle size), in this case you can either compress the bundle so it has less bytes to transfer over the HDD or Web or you can try to play with chunk sizes and restrict the player speed. – Arman Papikyan Apr 15 '19 at 12:43
  • Usually, say it takes 10 second for your player to cross the chunk, breaking your bundle into 5 (sec) * 60 (FPS) = 300 parts and downloading by (Size / 300) part each frame should be enough. – Arman Papikyan Apr 15 '19 at 12:43