Best Asset Bundle downloader?

Hi, hi found some methods on the documentation for download the bundles:

WWW.LoadFromCacheOrDownload (https://docs.unity3d.com/ScriptReference/WWW.LoadFromCacheOrDownload.html)

Caching (https://docs.unity3d.com/ScriptReference/Caching.html)

What difference are bethween therse?
I need to download a 3D map and cache it in a mobile game, and download it again if there is a new version,
how can i use it in the best way?

It is recommended to use UnityWebRequest for downloading asset bundles. It was designed to replace WWW.

1 Like

Yeah, but what are the differences?
And in the documentation of that there is a reference to www method, where is write to download using www
I’m confused :eyes:

UnityWebRequest has lower overhead on the rest of your game.
Documentation will be updated in the future to show examples using UWR instead of WWW.

1 Like

Will be great, thanks

Hi, i used this

IEnumerator LoadFromCacheOrDownload(string urlBundle, int version)
    {
        StartDownloadingUI();
        while (!Caching.ready)
            yield return null;

        WWW www = WWW.LoadFromCacheOrDownload(urlBundle, version);
        //yield return www;
        while(!www.isDone)
        {
            SetDownloadValue(www.progress);
            yield return null;
        }

        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.LogWarning("ERROR: " + www.error);
            yield return null;
        }

        AssetBundle myLoadedAssetBundle = www.assetBundle;
        GameObject museoLouvre = null;
        if (myLoadedAssetBundle != null)
            museoLouvre = myLoadedAssetBundle.LoadAsset(currentScene.sceneName + " GFX") as GameObject;
        else {
            Debug.LogWarning("The bundle " + currentScene.sceneName + " doesn't exist");
            //museoLouvre = myLoadedAssetBundle.mainAsset;
        }

        if (museoLouvre != null)
            Instantiate(museoLouvre, mapParent);
        else { Debug.LogWarning("The Game Object in " + currentScene.sceneName + " doesn't exist"); }
          
        StopDownloadingUI();
    }

… and work perfectly on computer,
but when i load the apk on Android, it load the bundle, but don’t instantiate it in the game, why?

p.s. (i know you told me to not use the www class, but the cache version don’t work for me, i add the script i try to use for that anyway)

IEnumerator DownloadAndCacheAssetBundle(string uri, string manifestBundlePath)
    {
        //Load the manifest
        AssetBundle manifestBundle = AssetBundle.LoadFromFile(manifestBundlePath);
        AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

        //Create new cache
        string today = DateTime.Today.ToLongDateString();
        Debug.Log("Cache name: " + today);
        Directory.CreateDirectory(today);
        Cache newCache = Caching.AddCache(today);

        //Set current cache for writing to the new cache if the cache is valid
        if (newCache.valid)
            Caching.currentCacheForWriting = newCache;
        else
            Debug.LogWarning("Cache invalid!"); //Added

        //Download the bundle
        Hash128 hash = manifest.GetAssetBundleHash("bundleName");
        UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri, hash, 0);
        yield return request.Send(); //SendWebRequest();
        AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);

        //Get all the cached versions
        List<Hash128> listOfCachedVersions = new List<Hash128>();
        Caching.GetCachedVersions(bundle.name, listOfCachedVersions);

        if (!AssetBundleContainsAssetIWantToLoad(bundle))     //Or any conditions you want to check on your new asset bundle
        {
            //If our criteria wasn't met, we can remove the new cache and revert back to the most recent one
            Caching.currentCacheForWriting = Caching.GetCacheAt(Caching.cacheCount);
            Caching.RemoveCache(newCache);

            for (int i = listOfCachedVersions.Count - 1; i > 0; i--)
            {
                //Load a different bundle from a different cache
                request = UnityWebRequest.GetAssetBundle(uri, listOfCachedVersions[i], 0);
                yield return request.Send(); //SendWebRequest();
                bundle = DownloadHandlerAssetBundle.GetContent(request);

                //Check and see if the newly loaded bundle from the cache meets your criteria
                if (AssetBundleContainsAssetIWantToLoad(bundle))
                    break;
            }
        }
        else
        {
            //This is if we only want to keep 5 local caches at any time
            if (Caching.cacheCount > 5)
                Caching.RemoveCache(Caching.GetCacheAt(1));     //Removes the oldest user created cache
        }
    }

In your WWW code you should yield break in case of error.

As for why it doesn’t work Android: have you built the AssetBundle for Android? The same bundle can not be used for both Desktop and Android.

1 Like

Thanks, stupid mistake XD

Mh… another question:
I add the yield break, and it all works perfectly, on android too… but if i try to reload, i have an error, because it is trying to use the www class, and create a duplicate of the asset bundle… how can i avoid that?
I used a player pref to remember the version loaded in the path, and add a second class to load the Asset Bundle locally, but how can i get the cache path?

void Start()
    {
        if (MenuManager.currentScene != null)
        {
            currentScene = MenuManager.currentScene;

            if (PlayerPrefs.GetInt(currentScene.bundleName, 0) < currentScene.bundleVersion)
            {
                PlayerPrefs.SetInt(currentScene.bundleName, currentScene.bundleVersion);
                StartCoroutine(LoadFromCacheOrDownload(currentScene.bundleURL, currentScene.bundleVersion));
            }
            else
            {
                StartCoroutine(LoadFromCache(currentScene.bundleURL, currentScene.bundleVersion));
                Debug.Log("Version " + currentScene.bundleVersion + " alredy downloaded");
            }
        }
        else { Debug.LogWarning("Menu Manager not founded, reload from Menu scene"); return; } 
    }

IEnumerator LoadFromCache(string urlBundle, int version)
    {
        StartDownloadingUI();
        while (!Caching.ready)
            yield return null;
        AssetBundle myLoadedAssetBundle = AssetBundle.LoadFromFile( ??? ) ;
        GameObject museoLouvre = null;
        if (myLoadedAssetBundle != null)
            museoLouvre = myLoadedAssetBundle.LoadAsset(currentScene.sceneName + " GFX") as GameObject;
        else
        {
            Debug.LogWarning("The bundle " + currentScene.sceneName + " doesn't exist");
        }

        if (museoLouvre != null)
            Instantiate(museoLouvre, mapParent);
        else { Debug.LogWarning("The Game Object in " + currentScene.sceneName + " doesn't exist"); }

        StopDownloadingUI();
    }

Why do you have two code paths?
Normally you would always use WWW.LoadFromCacheOrDownload() and it will download AssetBundle the first time and every next run it will load it from cache.

I had an issue, and i think was the best way to solved, but i only have to add AssetBundle.UnloadAllAssetBundles(true):

Code Updated:

void Start()
    {
        StopDownloadingUI();

        if (MenuManager.currentScene != null)
        {
            currentScene = MenuManager.currentScene;

            AssetBundle.UnloadAllAssetBundles(true);
            StartCoroutine(LoadFromCacheOrDownload()); //WORK!!!
        }
        else { Debug.LogWarning("Menu Manager not founded, reload from Menu scene"); return; } 
    }