AssetBundle Caching Process. What happens when a Unity asset bundle is cached? What properties of an ab make it different from its cached form? - unity3d

This is probably a very niche question, but what are the specific differences between an asset bundle and a cached bundle for the asset bundle system that Unity 4 used? I'm curious how cached files differ to asset bundles and what, if any, different properties change from both types of files? I.E. What happens to asset bundles when they are cached through WWW.LoadFromCacheOrDownload?
Unity unfortunately doesn't have this process documented as the open source code calls an internal function called INTERNAL_CALL_WWW(this, url, ref hash, crc), which is given the instance of WWW, the string of the URL, a Hash128 and a UINT called crc with a default value of 0. (Referenced decompiled 5.3.2p2 code, 2017+ code is on the official Unity GitHub, but seems to differ from Unity 2015 code).
The Unity 2017 code leads to the private extern static IntPtr CreateCached(DownloadHandlerAssetBundle obj, string url, string name, Hash128 hash, uint crc); external method, which isn't documented either.
Furthermore, what actually happens when an asset bundle is cached so that the engine can recognise the file is in cache and doesn't need to be downloaded? Is it just simply that it searches for the hashed name in the cache, or does it do something more intricate?
Frostrix

I can clearly see the difference in size between created asset bundles (those to put on server to download later) and cached asset bundles (those downloaded once at runtime and stored to the device). On Windows you can find them at %appdata%/Local Low/Unity/company+name_of_app. Cached versions are typically less compressed so the app can load them faster, while those stored on the server are more compressed so it takes less bandwidth to download. The content should be the same, but compressed using different methods. See this docs page for more details.

Related

Is there any way to split data files into smaller chunks in Unity WebGL builds?

I am limited to the size of web applications I can build by the "Build\application.data" file.
I.e if its over a certain size I cannot upload it certain hosts, github, etc.
Ideally I would like to split the application into multiple data files under a certain size, while the application is still executable.
How would this be possible? Is this something I can do from Unity build configuration?
Can I do it after the build is done?
Can I split the file into chunks by archiving it with zero compression, and somehow still execute it from the browser? There is a file called Build.Loader.js, is it something that can be edited for this purpose?
This is for the purposes of using the application after it has been uploaded, not sharing it, I do not want to compress it into separate archives, or use gitlfs, I've tested this and the application does not work from the browser with github and gitlfs.
Thanks
Unity has 2 technologies for split data file:
Asset bundle
An AssetBundle is an archive file that contains platform-specific
non-code Assets (such as Models, Textures, Prefabs, Audio clips, and
even entire Scenes) that Unity can load at run time
Addressbles
The Addressable Asset System allows the developer to ask for an asset
via its address. Once an asset (e.g. a prefab) is marked
"addressable", it generates an address which can be called from
anywhere. Wherever the asset resides (local or remote), the system
will locate it and its dependencies, then return it.
Both technologies create separate files that you can host on a server and download as needed. Addressable is a newer technology that Unity team recommends.
Probably the total size of the bundle will grow, but user will be able to download only the necessary assets and the amount of data for the user may decrease
If you do not use Unity solutions, you can divide data file into parts. But on the client side (javascript) you will need to download all the parts, connect them and pass to Unity loader. You probably won't be able to use the browser's built-in gzip or brotli (not sure). It seems to be quite difficult.

Unity Asset Bundles Rebuilding with Old Data

I have a mobile project which displays various AR experiences to the user via a mobile application. This application is made in Unity.
Alongside this Unity project we have a second project that stores asset bundles exclusively. Each AR experience is a prefab marked with a unique asset bundle name. These bundles are stored online and downloaded into the main mobile application when relevant. We're having issues with having these bundles correctly update, when changing things on the prefab such as scale or rotation they aren't reflected in the rebuilt asset bundle.
Here's a look at the process we're using for rebuilding assets, It's only a simple script.
[MenuItem("Spiff/Build AssetBundles")]
static void BuildAllAssetBundles()
{
// BuildPlatformBundles(BuildTarget.iOS);
BuildPlatformBundles(BuildTarget.Android);
}
static void BuildPlatformBundles(BuildTarget platform)
{
// We convert the passed platform enum to a string.
string platformFolder = platform.ToString();
string assetBundleDirectory = "Assets/AssetBundles/" + platformFolder;
// Build our bundles out to the relevent platform specific folder.
if (!AssetDatabase.IsValidFolder(assetBundleDirectory))
{
AssetDatabase.CreateFolder("Assets/AssetBundles/", platformFolder);
}
BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, platform);
}
Currently we use 'BuildAssetBundleOptions.None' We've also tried the ForceRebuild flag but this has no effect. Am I correct in assuming that Unity has some sort of internal cache it keeps for assets bundles? If so can we clear this somehow so I can ensure 100% that the bundle I am building is going to be the most up to date based on the prefab tagged with it?
Loaded Assetbundles are indeed cached, and will remain so until AssetBundle.Unload() has been called to free up all memory accociated with the asset.
After the asset has been unloaded a newer version of the assetbundle can be downnloaded and instantiated with the new values (Assuming the file downloaded has all the updated information), else it will load the data from cache if available.
On a side note: It seems like you're still using the old assetbundle pipeline. Unity has a plugin tool that makes the assetbundle workflow considerably easier called the assetbundle browser found here. It comes with a way easier UI for building bundles and inspecting bundles, open source and a very customisable pipeline. It may be worth checking out.
Like remy_rm mentioned, do give the Unity Asset Bundle Browser tool a try. It gives more control over what assets are in the bundle.
Your problem can be solved if you ensure two things:
1- When you make changes to a prefab and create a new bundle, is it indeed created with the said changes? (again using the tool would make checking this easier)
2- The bundles you are downloading in the main application are the new ones and not the old cached ones. In Unity Webgl for instance, you need to clear the browser cache to be 100% sure that the application downloads the new asset bundles and doesn't use the old ones in browser cache.

Unity Asset Bundles - Which Files Do I Deploy?

I have created some asset bundles from my Unity assets using the directions given in the Unity documentation section on AssetBundle Workflow. After running the "Build AssetBundles" command, each asset bundle results in four files: myasset, myasset.meta, myasset.manifest, myasset.manifest.meta.
Now I am ready to deploy these bundles to a web server and implement downloading/caching in my Unity project. I have found numerous examples such as this that show the download URL to be a single file with a .unity3d extension. This is leading me to conclude that I am missing a step - I assume that all four of my files will be required by the app and that I have to do something to combine them into a .unity3d file first.
What file(s) do I need to deploy? Are there any additional steps that I need to take before my file(s) are ready to upload? Thanks in advance for any advice!
Just myasset will suffice.
Sometimes people optionally add .unity3d as a filename extension to their Asset Bundles. It is just a community convention, and is completely optional. Source (copied below)
Vincent-Zhang
Unity Technologies
Just a reminder, we don't have an official file extension ".unity3d" for asset bundle, it's not mandatory. You can use whatever file extension as you want, or without file extension.
But usually people use ".unity3d" as the file extension just because we used it in the official sample code at first time...
Unity creates the .meta files for all assets- you don't have to worry about those. In short, your myasset file is enough. I do not add file extensions to mine. Do note that if you use the strategy shown in the example that you shared that the client will re-download the bundle from the server every time. You need to additionally provide a version number if you want to take advantage of caching. You can see this in some of the method overloads here, the ones that have a Hash128 or uint "version" parameter. Caching is great because you can use the bundle that is already saved on the device next time instead of downloading from the server when no changes have occurred. The version/hash number you provide essentially gets mapped to the file name. Whenever a matching version is found, the file on disk is used. Update the version number to something else when the content changes to force the client to download anew.
You may want to reference the .manifest file for the CRC value listed there. You may have noticed a crc parameter in the link I shared as well. This can be used to ensure data integrity during transmission of the bundle data. You can make sure the downloaded content's CRC matches that of the bundle when you created it.

Which way is better to handle AssetBundle Download Management

Before going to ask the question, I will explain what kind of requirements I need.
In my game, there are a lot of textures needed in order to play it. Because of this and the limitation of ipa size (iOS), I think using assetbundle is a must. Because the existing code implementation, I create the assetbundle, 1 asset bundle for 1 image. For example: I have 100 images, then I will create 100 assetbundle(with the mainasset will be the texture), with the same name.
I want my game can be played offline AFTER at first time the user has downloaded all the needed assetbundles. So if the user play for the first time or download the updated version of my game, the game will be forced into special scene where it will download all needed assetbundle, after that, the user can play the game without internet connection.
I thought there are 2 ways regarding of how to download these assetbundle:
The normal way: I put all 100 assetbundles in my server, and put 1 xml file, which consist all assetbundles information and its version. At the special scene, the game will download the xml file, and from that it will download 100 assetbundles via WWW.LoadFromCacheOrDownload. The purpose is just to store the needed assetbundles to the cache of game. After successfully downloaded all of them, user can play offline and if the game need the assetbundle to be loaded, then I just callagain the WWW.LoadFromCacheOrDownload, since it is still in the cache, it can be played offline. Of course, I assume the cache is still there as long as I dont clear the cache explicitly.
The hard way: I will zip all 100 assetbundle to 1 zip file. The game will download the xml file, and the zip file. I will decompress the zip, and put all 100 assetbundle in the mobile storage (iOS and Android). So after that, in case the game need to load the asset, I just call the WWW.LoadFromCacheOrDownload with the url is using file:/// scheme that point to the path of mobile storage.
Summary:
The Normal Way:
Minus: I assume the cache is still there, but if there is something wrong in the cache, the user can't play the game.
Plus: simple implementation, because the assumption that cache will be fine.
The Hard Way:
Minus: well, i don't know it is minus point or not, but this one, I have to implement the unarchived zip file and store them in storage. More implementation needed at server side and the application side. And since I am newbie, I don't know the best practice in this area.
Plus: more robust
So which one is better ? or any better recommendation?
We have essentially implemented a mix of your normal and hard way for our application. As yours, it's rather texture heavy, so most groups of texture assets are packed into their own asset bundles. (In our application texture have a logical grouping, so we don't pack them individually). We however don't download them directly to the cache. We download these to the sdcard and from there load them (or get them from cache) as we need them. (i.e. LoadFromCacheOrDownload).
It depends a bit on your situation, but I would generally advise against packing them into a single zip file. Primarily with regards to updates. It has happened in our case that an asset needed some updates, or that we wanted to add assets later on. By having an XML file or database containing versioning data, updating the data on the device is trivial. And if only one bundle changes, you only need to download that single bundle. And 2MB vs 450MB in our case is quite the difference.

How does GWT ClientBundle caching work?

I am trying to better understand the use of GWT ClientBundle and caching.
If I have a large text file, for example, that I'd like to make available to my client, I can use
public interface MyResources extends ClientBundle {
public static final MyResources INSTANCE = GWT.create(MyResources.class);
#Source("myText.txt")
public TextResource myText();
}
//-- then later to use the text
String text = MyResources.INSTANCE.myText().getText();
Does this mean that the file "myText.txt" would be downloaded from the server the first time the client runs the app, and then the file would be stored in the browser's cache so that in future uses of the app, the file does not need to be downloaded?
If so, what happens if I change "myText.txt", does the app know to get the new version?
Finally, if the file is indeed stored in the cache, how then is this different from local storage in HTML5?
Thanks.
As Daniel Kurka already mentioned, the resources can be inlined in the js file (a *.cache.* file) where the rest of the compiled GWT code lives.
Inlining does not occur for all resources in a client bundle. E.g. large images are never inlined, it can also be prevented with #ImageOptions.preventInlining(), and it doesn't occur for ExternalTextResources.
What's common for both cases is, that the results will be in *.cache.* files, with unique names that change automatically whenever the contents of a source file change (you'll have to recompile the GWT app though!)
This allows the server to deliver these files with appropriate caching HTTP headers (you'll have to set this up yourself!) For the client this means, that it will not only be able to cache the contents (which it does anyway, even if those headers aren't set), but it can even skip asking the server, if a newer version exists.
The big advantage of ClientBundles is, that the file names will change automatically. The biggest disadvantage is, that you must recompile your GWT app, when a resource changes. If you don't want that, then it's better to use some other approach to load the files: You can still make the browser cache any file you like (by setting the HTTP headers), but then you'll have to be careful to manually give them a new name, when the content changes.
You should use an External Text Resource if you want it to be loaded on demand and not as a part of compiled JavaScript.
https://developers.google.com/web-toolkit/doc/latest/DevGuideClientBundle#TextResource
If your users need the entire file, use one text resource. If users need parts of it, split this file into separate smaller files: only the requested file will be loaded when needed.
The external text resources can be cached like all other static files.
Files that are inside a clientbundle get inlined into your compiled javascript. They will not be downloaded separately. If you want to download a resource at a given time you can easily use request builder for that.
If you don`t want to download the file immediately but you still want to inline it, you can use code splitting and put the bundle into another part of your app.