Asset Bundles and the headache I found therein

So the last little bit I have been working on a web game that is comprised of 11 different scenes (10 levels and a main menu).

To make the game extremely lite-weight on initial load we decided to bundle the music for each level and each level themselves separately. This resulted in 21 separate asset bundles. (2 bundles per level + the main menu’s bg music)

To make things simple for programming, a basic asset loader component was constructed and placed along side each scene’s main managing script. On initial load of main menu, the music is loaded in. The user will then be able to navigate around the main menu and eventually choose a level. Upon selecting a level, a bundle containing the level that was selected is loaded in and then the Application.LoadLevel(“levelname”) is called. This works, to an extent.

Continuously upon returning to a scene that was already visited once before I received this exception:

The assetbundle ‘file_name_here.unity3d’ can’t be loaded because another asset bundle with the same files are already loaded
UnityEngine.WWW:get_assetBundle()

Okay – so… I can’t load in this bundle because it’s already been loaded. That’s fine, I’ll just check if the bundle has been loaded before I load it. Sounds simple enough. – Famous last words.

It turns out that after scouring the docs, this is no way in the run-time API to check if an assetbundle of a certain type has been loaded already. I decided to take my case to the Unity community posting an inquisition for a solution, as well as notifying the Twitterverse with a link to my forum post. Within just a couple hours my post on the forums shot up to just shy of 80 views with not a single reply. Back to the think-tank I went.

After some thought, I decided to utilize PlayerPrefs to keep track if a bundle had been loaded.

In the real world situation I used a GameData class that serves as a gateway – but essentially the provided code below is the same thing without the filler –

When the loaded scene’s manager script calls Awake() it is logical to assume that the level’s bundle has been loaded. I make this call there:

[chsarp]

PlayerPrefs.SetInt("level"+(int)LEVELID+"loaded",1);

[/csharp]

Now checking is as simple as this:

if(PlayerPrefs.GetInt("level"+(int)LEVELID+"loaded") == 0)
{
DoLoad();
}
else //already downloaded
{
Application.LoadLevel("LevelName");
}</code>

There's a final hole to fill here - and it actually is pretty easy to overlook. The thing about PlayerPrefs is that it is persistent data and therefor remains even after the application is closed. Since the asset bundles need to be reloaded each time the application is freshly started, we need to recursively move through and make sure that all the level's corresponding PlayerPrefs properties are set back to 0 (false).

To do this, I use Time.time and check to make sure that the application is so young that the user couldn't possibly have ran this same code twice by exiting and returning to the scene. So in the Main Menu's manager script I used the Awake() method and ran this:

if(Time.time &lt; 1)
{
//recursively set all the appropriate PlayerPrefs to 0
}</code>

This ensures that it is only going to execute at the very beginning of the application.

In any case, that was my remedy for tackling this problem. Personally I feel that there should be something in the run-time API allowing me to detect this. I can only imagine how much of a headache it would be had I been doing something with more than 10 levels.

'til next time

==

Leave a Reply