Search Unity

assetbundle as permanent DLC

Discussion in 'Editor & General Support' started by endupgaming, Oct 3, 2014.

  1. endupgaming

    endupgaming

    Joined:
    Mar 12, 2012
    Posts:
    57
    Hi

    This is for standalone only.

    There is a lot of detail on the forum and web about saving asset bundles but it does not seem to be aimed at true DLC type of functionality. LoadFromCacheOrDownload() puts the bundle in a cache but it will expire in max 150 days, most people do not want their DLC to expire.

    Is there a simple way to use a regular folder to store the assetbundle and then either load it from there or the web if it does not exist?

    Cheers
    Matt
     
    makoto_snkw likes this.
  2. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    329
    You could try using www.bytes instead of www.assetbundle to get the byte data for the AssetBundle, save that to disk and use AssetBundle.CreateFromFile to load it again the next time you start the application.
     
  3. endupgaming

    endupgaming

    Joined:
    Mar 12, 2012
    Posts:
    57
    Thanks billykater, I'll give that a go.
     
  4. endupgaming

    endupgaming

    Joined:
    Mar 12, 2012
    Posts:
    57
    Well, closer but still no luck

    To use assetbundle createfromfile it needs to be uncompressed. But I want to store it on the web compressed, but there does not seem to be a simple assetbundle.uncompress() method.

    Perhaps I should use zip and an open source lib for mono?

    It feels like this should be easier which often means I'm missing something.

    Cheers
     
  5. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    329
    If compression is the only problem left, that is something we can work with. :)

    The WWW class can load things directly from your file system using the file://path/to/your/file. That is exactly what we are using in our games and I haven't seen the uncompressed flag anywhere in our build system, so this should definitely work. (I wasn't sure about the www.bytes part as I have never used it on an assetBundle download before)
     
  6. endupgaming

    endupgaming

    Joined:
    Mar 12, 2012
    Posts:
    57
    Yeah so this is what I have now.

    In the editor script

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.IO;
    5. using thelab.system;
    6. public class ExportAssetBundles
    7. {
    8.     [MenuItem("Assets/Build AssetBundle From Selection - Track dependencies")]
    9.     static void ExportResource()
    10.     {
    11.         // Bring up save panel
    12.         string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "unity3d");
    13.         if (path.Length != 0)
    14.         {
    15.             // Build the resource file from the active selection.
    16.             Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
    17.             BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path,
    18.                               BuildAssetBundleOptions.CollectDependencies |
    19.                               BuildAssetBundleOptions.CompleteAssets |
    20.                               BuildAssetBundleOptions.UncompressedAssetBundle,
    21.                               BuildTarget.StandaloneWindows);
    22.             Selection.objects = selection;
    23.  
    24.             ByteArray ba;
    25.  
    26.             using (FileStream file = File.Open(path, FileMode.Open))
    27.             {
    28.                 ba = new ByteArray((uint)file.Length);
    29.                 byte[] data = new byte[file.Length];
    30.                 file.Read(data, 0, (int)file.Length);
    31.                 ba.position = 0;
    32.                 ba.Write(data);
    33.             }
    34.             ba.Compress();
    35.  
    36.             File.WriteAllBytes(path, ba.data);
    37.         }
    38.     }
    39. }
    40.  
    Which is using the free Forge asset available on the asset store to do our own compression.

    I upload that file to the web

    Then when the game starts I'm


    Code (CSharp):
    1. IEnumerator Start()
    2.     {
    3.         // do we already have the DLC
    4.         if (File.Exists("Download/DLC/SphereBundle.unity3d"))
    5.         {
    6.             Debug.Log("I'll get it from the file");
    7.             AssetBundle bundle = AssetBundle.CreateFromFile("Download/DLC/TestBundle.unity3d");
    8.             if (AssetName == "")
    9.                 Instantiate(bundle.mainAsset);
    10.             else
    11.                 Instantiate(bundle.Load(AssetName));
    12.             bundle.Unload(false);
    13.         }
    14.         else
    15.         {
    16.             Debug.Log("I'll get it from the web");
    17.             using (WWW www = new WWW(BundleURL))
    18.             {
    19.                 yield return www;
    20.                 if (www.error != null)
    21.                     throw new Exception("WWW download had an error:" + www.error);
    22.  
    23.                 Directory.CreateDirectory("Download/DLC");
    24.            
    25.                 ByteArray ba = new ByteArray((uint)www.bytes.Length);
    26.                 foreach(byte b in www.bytes)
    27.                 {
    28.                     ba.Write(b);
    29.                 }
    30.            
    31.                 ba.Decompress();
    32.                 File.WriteAllBytes("Download/DLC/TestBundle.unity3d", ba.data);
    33.  
    34.                 AssetBundle bundle = AssetBundle.CreateFromFile("Download/DLC/TestBundle.unity3d");
    35.                 if (AssetName == "")
    36.                     Instantiate(bundle.mainAsset);
    37.                 else
    38.                     Instantiate(bundle.Load(AssetName));
    39.                 bundle.Unload(false);
    40.  
    41.             }
    42.         }
    43.     }
    look to see if there is a local file, if not I download it, decompress it and save it to the file.

    I'm using CreateFromFile to then load it.

    This is working very nice. I have only tested this in Unity 4.5 windows.

    The code is also very raw just testing, so probably some more elegant methods out there.

    Cheers
     
    Last edited: Oct 8, 2014
  7. endupgaming

    endupgaming

    Joined:
    Mar 12, 2012
    Posts:
    57
    I should perhaps point out in the first instance I'm saving the asset bundle with unity BuildAssetBundle uncompressed, then opening and saving it again using the compress method from Forge. This is because if I compress it with unity first, I could not un-compress it with forge. I think unity uses LZMA compression, but perhaps they do something else as well.

    Anyway, its working pretty well, I get about the same compressed size for the web but then can download and keep on hardrive uncompressed.

    Cheers
     
  8. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    329
    As said using something like the following (modified in notepad++ from your sample code so might contain errors)

    Code (csharp):
    1.  
    2. IEnumerator Start()
    3. {
    4.    var www = new WWW("file://Download/DLC/SphereBundle.unity3d");
    5.    yield return www;
    6.    if(www.error != null)
    7.    {
    8.      www = new WWW(BundleURL);
    9.      yield return www;
    10.      
    11.      if(www.error != null)
    12.      {
    13.        throw new Exception("WWW download had an error:" + www.error);
    14.      }
    15.      Directory.CreateDirectory("Download/DLC");
    16.      File.WriteAllBytes("Download/DLC/SphereBundle.unity3d", www.bytes);
    17.    }
    18.    var bundle = www.assetBundle;
    19.    if (AssetName == "")
    20.      Instantiate(bundle.mainAsset);
    21.    else
    22.      Instantiate(bundle.Load(AssetName));
    23. }
    24.  
    Should allow you to work with compressed asset bundles all the way.

    What my programming sense tells me about your code
    • Is using the "custom" compress method faster or slower than unity's built in one?
    • How significant is the compressed vs uncompressed load time?
    • Most importantly how does this solution fare in regards to memory allocation. Depending on your game a DLC asset for a PC game can quickly get several hundreds of MB. Using your method might duplicate the DLC in memory more often (depending on how unity implements it).
    I personally would go for the all unity solution unless there are some roadblocks that can't be solved. Btw thanks for the idea of using uncompressed asset bundles, might come in handy if I one day need to load them on demand and need additional speed ^^.
     
  9. endupgaming

    endupgaming

    Joined:
    Mar 12, 2012
    Posts:
    57
    Hi Billy

    In answer to your questions.

    1. I'm using 3rd party compress / decompress because I dont see anything in Unity that can do this. There is the built in BuildPipeline.BuildAssetBundle() which will automatically compress if you set the flag, but once compressed, no way to decompress and no way to use the compressed one unless you use the LoadFromCacheOrDownload, but then there are restrictions on cache lifespan and this is just not a DLC solution.

    2. As I was only testing on an asset that was 2k compressed and 9k uncompressed I cant really say as both appeared instantly. I will be doing some further testing to see what the differences are.

    3. Memory is also yet to be tested. I'll look at a larger set of assets and see how they are used.

    It would be good to understand in a unity level loaded normally are all the objects loaded at once in memory or loaded only when instanced. Would it be the same memory footprint to a regular level to LoadAll() out of the asset bundle and then Unload(false).

    I'll post back here when I've done some tests. I've never used the memory profiler before so will see how I go.

    Cheers
    Matt
     
  10. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    329
    As described in my code example above, this should be all you need to load the compressed assetbundle from local disk (even though it is using the www class). So LoadFromCacheOrDownload shouldn't be necessary. And you won't have any issue with cache invalidation.

    So if you don't have any reason to not use WWW and the "file://" protocoll, all the custom compression/uncompression seems to be unnecessary.

    The rest of the questions was just me being interested in how these things work ^^
     
  11. endupgaming

    endupgaming

    Joined:
    Mar 12, 2012
    Posts:
    57
    Ahh, thanks mate, I missed the file:// part. I'll give that a go.

    Cheers