Unity3D File Based Assetbundles on the iPhone


Let me start off by saying that Unity3D is a really great game engine. Let me also say that I wish the documentation was as great as the engine is. Sadly, it’s difficult to figure out some of the more powerful features of Unity3D without significant trial and error.

Please note, that I have read that the usage of Assetbundles on the iPhone can be memory intensive and may or may not fit your needs per your project. I am just putting this information here so that others can see how to do this. Pro Tip: What this means to you? Profile the heck out of your code, pre-assetbundles and after integrating assetbundles.

Assumptions:
1. You know or are familiar Unity3D 3.3.
2. You can code in C#.
3. You’re an ace at creating prefabs in Unity.
4. You’re using Unity3D 3.3 Pro and iOS Pro.

OK, if you’re still feeling good, let’s bounce into this how-to.

Create the Project

Open up a new project in Unity3D and set it up for “iPhone” iOS. Feel free to name it whatever passes your way.

In the project hierarchy, let’s go ahead and create a simple directory structure as follows:

/Example Project/
/Example Project/iphone/
/Example Project/iphone/objects/
/Example Project/iphone/materials/

Hit save after doing this and save your default scene and name it whatever you want.  In my case, I named it “test”.

Though, this is a near complete looking example (image to the right), the basic structure should look like the image to the right here.

Add at least one prefab to your project. I have added two prefabs here, “box” and “ABCD”. Both are just tests and contain a cube prefab with a simple texture applied. Make sure that the materials referenced by your prefabs have the following attributes. 1. They are named the same as the prefab. (See the image above!). 2. They are stored in the materials folder underneath the iphone folder.

UnityEditor Coding Time

When working on assetbundles, the first thing you have to do is generate them. Unfortunately, Unity3D does not have anything built in for generating these files except for a UnityEditor API. The UnityEditor script can only be run from inside the Unity3D editor tool. It typically appears as a menu item after the script has been successfully compiled. So if you can code, then you’re in good shape.

Let’s create the UnityEditor script. Create a new C-Sharp script in a /Plugins/ folder (create it if it doesn’t already exist) at the root of your project and name it MPCreateAssetBundle. Copy and paste the following into it. Please note: some of this code was borrowed from Unity’s own Character Customization tutorial on their website.

View Code CSHARP
using System;
using System.IO;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object=UnityEngine.Object;
 
public class MPCreateAssetBundle : MonoBehaviour {
 
	private static String[] mpAssetDir = new String[]{"iphone", "ipad", "macosx", "pc"};// , "universal"}; <-- not needed, use the /Resources/ folder.
 
	[MenuItem("Custom/Monkey Prism/Create All Assetbundles #&i")]
	public static void Execute() {
		Debug.Log("Creating Assetbundles");
		bool blnFound = false;
		String currentDir = null;
		foreach (Object o in Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets)) {
			blnFound = true;
 
			if (o.name.Contains("@")) continue; //animations!
			String assetPath = AssetDatabase.GetAssetPath(o);
			if (!File.Exists(assetPath)) {
				currentDir = assetPath;
				continue; //Files only.
			}
			//Only check those directories that we have specified in the mpAssetDir
			Debug.Log(assetPath);
 
			String currentBuildType = null;
			foreach (String s in mpAssetDir) {
				if (assetPath.Contains("/"+s+"/")) {
					currentBuildType = s;
					break;
				}
			}
			if (currentBuildType == null) continue; //if the directory is not found to be one from the mpAssetDir bail out.
 
			string assetBundleName = null, genericName = null;
 
			List<Object> toinclude = new List<Object>();
 
			//Generate pre-fabs for everything in the finished pre-fab directory.
 
			if (o.GetType() == typeof(GameObject)) {
				Debug.Log("GameObject " + currentDir);
 
				String d = CharacterRoot((GameObject)o);
				d += "materials/";
				Debug.Log(d);
				List<Material> materials = CollectAll<Material>(d);
				Debug.Log("materials count=" + materials.Count);
 
				genericName = o.name.ToLower();
				assetBundleName = currentBuildType + "-prefab-" + genericName;
				//Package up the prefabs in the iPhone directory.
				toinclude.Add(o);
				//Do we need to add in a material?  I think so.
				foreach (Material m in materials) {
					Debug.Log("Material Name=" + m.name);
                    if (m.name.Contains(genericName)) {
						toinclude.Add(m);
						Debug.Log("Added a new material!");
					}
				} //end foreach
 
			} 
 
			if (assetBundleName == null) continue;
 
 
			// Create a directory to store the generated assetbundles.
            if (!Directory.Exists(AssetbundlePath))
                Directory.CreateDirectory(AssetbundlePath);
 
            // Delete existing assetbundles for current object
            string[] existingAssetbundles = Directory.GetFiles(AssetbundlePath);
            foreach (string bundle in existingAssetbundles) {
                if (bundle.EndsWith(".assetbundle") && bundle.Contains("/assetbundles/" + assetBundleName))
                    File.Delete(bundle);
            }
 
			//Directories expected.
			Debug.Log("currentBuildType = " + currentBuildType);
 
			//path = AssetbundlePath + bundleName + ".assetbundle";
			if (toinclude.Count > 0) {
				String path = AssetbundlePath + assetBundleName + ".assetbundle";
				Debug.Log(path);
				if (currentBuildType.Equals(mpAssetDir[0]) || currentBuildType.Equals(mpAssetDir[1])) //iPhone & iPad
					BuildPipeline.BuildAssetBundle(null, toinclude.ToArray(), path, BuildAssetBundleOptions.CollectDependencies, BuildTarget.iPhone);
				else //TODO: might need to condition further and might want to use an enum with the conditional.
					BuildPipeline.BuildAssetBundle(null, toinclude.ToArray(), path, BuildAssetBundleOptions.CollectDependencies);
			}
 
 
		} //end foreach
 
		if (!blnFound) {
			Debug.Log("no objects were found for building assets with.");	
		}
	}
 
	public static string AssetbundlePath
    {
        get { return "assetbundles" + Path.DirectorySeparatorChar; }
    }
 
	// This method loads all files at a certain path and
    // returns a list of specific assets.
    public static List<T> CollectAll<T>(string path) where T : Object
    {
        List<T> l = new List<T>();
        string[] files = Directory.GetFiles(path);
 
        foreach (string file in files)
        {
            if (file.Contains(".meta")) continue;
            T asset = (T) AssetDatabase.LoadAssetAtPath(file, typeof(T));
            if (asset == null) throw new Exception("Asset is not " + typeof(T) + ": " + file);
            l.Add(asset);
        }
        return l;
    }	
	// Returns the path to the directory that holds the specified FBX.
    static string CharacterRoot(GameObject character)
    {
        string root = AssetDatabase.GetAssetPath(character);
        return root.Substring(0, root.LastIndexOf('/') + 1);
    }
 
}

Once this is completed, you’ll want to do two things.
1. Select (by moving your mouse over and left-clicking on it once) the iphone folder under “Example Project”.
2. Run the script as shown in the image:

So let’s back up for a second and look at what we just did. We created a project, created some folders within the project, placed prefabs into the iphone folder and created a UnityEditor script. Upon running this script, a new directory named “assetbundles” would be created.

This directory is not going to be visible to you until you open finder (on Mac) or explorer (on Windows) as it is located on the file system. (see the image above). Inside this folder you should see your prefab(s) named now as: iphone-prefab-<my prefab name>.assetbundle. If you are seeing this, congratulations, you’ve now created your own assetbundles uniquely per prefab.

Generic GameObject and a Simple Script

If you’ve come this far, great work! We’re nearly done.
Let’s create a new GameObject under the Hierarchy.

Now we’re going to write a simple script to instantiate the assetbundles we assembled earlier. Create a new C-Sharp script in the /Plugins/ folder and name it “Example”. Copy and paste the following code into it:

View Code CSHARP
using System;
using UnityEngine;
using System.Collections;
 
public class Example : MonoBehaviour {
	private WWW www;
	private AssetBundleRequest gameObjectRequest;	
	// Use this for initialization
	IEnumerator Start () {
        //TODO: change the name of the file below!!!
		String strPath = "file://" + Application.dataPath + "/../assetbundles/iphone-prefab-box.assetbundle";
		Debug.Log(strPath);
		www = new WWW(strPath);
		yield return www;
        /* TODO: change the name "Box" to match the name of your prefab that you compiled into the assetbundle on step 1. */
		AssetBundleRequest request = www.assetBundle.LoadAsync("Box", typeof(GameObject));
		yield return request;
		Instantiate(request.asset, new Vector3(3f, 0f, 0f), Quaternion.identity);
		Instantiate(request.asset, new Vector3(0f, 0f, 0f), Quaternion.identity);
		Instantiate(request.asset, new Vector3(-3f, 0f, 0f), Quaternion.identity);
		www.assetBundle.Unload(true);
	}
        void Update() { }
}

NOTE: Make sure to change the TODO line in the code to match your file name! If you don’t do this, it might not work!

We’re nearly done! Drag your Example.cs script onto the GameObject you created earlier. Here is how my project looks:

Build Time

We’re now going to build this codebase so that you can run it on your iOS device for testing. I’m going to skip the painful steps of creating a code signing certificate and all that jazz. We’re instead going to focus on how to get the actual assetbundle onto your iOS device in an area that will be accessible to the “Application.dataPath” call in your code!

1. Build the Unity project for iOS.
2. XCode should automatically open.
3. Within XCode you are going to need to tell it about your assetbundles folder! Open up finder or explorer and locate the assetbundles folder that was generated by the UnityEditor script we discussed earlier.
4. Drag and drop the assetbundles folder into XCode at the root of the iOS project. (You should see a prompt as follows)

5. Click “Finish”.

This will then look something like this:

6. Build the code using XCode and run it on your iOS device.

If everything works properly, you should see one of the prefabs instantiate on your iOS screen. That is all there is to it! I think you’ll find that this isn’t as bad as one might imagine, but it does require some serious fishing around in code examples and heavily crawling the Unity forums. The only way I figured this out was by trial and error, heavy amounts of Debug.Log calls and 3 pots of coffee. I’m now going to go and grind my teeth on my level editor.

Edit: I have one of my original forum posts here as well regarding this issue. Unity3D Forum

, , , , , , ,

  1. #1 by skartboard on May 16th, 2011

    Hi,

    Very nice tutorial.

    I try to follow your step, but have some compile error like this:

    Assets/Plugins/MPCreateAssetBundle.cs(4,7): error CS0246: The type or namespace name `UnityEditor’ could not be found. Are you missing a using directive or an assembly reference?

  2. #2 by Chris Danielson on May 16th, 2011

    @skartboard Thank you for the compliment! Do you have Unity Pro 3.3 installed? I’m thinking that this might be why that library is missing on your installation. I’m not sure though.

  3. #3 by skartboard on May 16th, 2011

    @Chris
    Yes, I’m sure my Unity is Pro 3.3. I guess UnityEditor can not be compiled in the software release!!PC MAC iOS , these platforms can not be released

  4. #4 by skartboard on May 16th, 2011

    Hi Chris,

    I found how to fix this error. To use a UnityEditor class/object you have to place your script in Assets/Editor inside your project folder.

  5. #5 by Chris Danielson on May 17th, 2011

    skartboard :

    Hi Chris,

    I found how to fix this error. To use a UnityEditor class/object you have to place your script in Assets/Editor inside your project folder.

    Great find! http://unity3d.com/support/documentation/ScriptReference/index.Script_compilation_28Advanced29.html

  6. #6 by Brenno on July 8th, 2011

    hi,

    I don’t have Costum menu :s. I can’t run theres scripts.
    Can you help me?

    I have the same version of software you.
    thank you
    Best Regard

  7. #7 by Chris Danielson on July 8th, 2011

    Brenno :

    hi,

    I don’t have Costum menu :s. I can’t run theres scripts.
    Can you help me?

    Are you receiving any compilation errors in Unity? Also, did you place the scripts in the proper locations? That is a common “gotcha” in Unity.

    “To use a UnityEditor class/object you have to place your scripts in the Assets/Editor inside your project folder.”
    http://unity3d.com/support/documentation/ScriptReference/index.Script_compilation_28Advanced29.html

  8. #8 by Brenno on July 8th, 2011

    I have created a new C-Sharp script in a /Plugins/ folder at the root of my project and name it MPCreateAssetBundle and Example.

    But i don’t have the “Costum” menu on my dashboard like this picture : http://www.chrisdanielson.com/wp-content/uploads/2011/05/Screen-shot-2011-05-03-at-1.45.19-PM.png

    i have just terrain and window menu.
    🙁

  9. #9 by Brenno on July 8th, 2011

    Brenno :
    I have created a new C-Sharp script in a /Plugins/ folder at the root of my project and name it MPCreateAssetBundle and Example.
    But i don’t have the “Costum” menu on my dashboard like this picture : http://www.chrisdanielson.com/wp-content/uploads/2011/05/Screen-shot-2011-05-03-at-1.45.19-PM.png
    i have just terrain and window menu.

    i have now the Costum menu!
    thanks a lot !

  10. #10 by Gaurav on July 18th, 2011

    i have unity iphone 1.7 but not ios pro! would assetbunder work on basic unity ios ? or we need ios pro licence for that !

  11. #11 by Gaurav on July 18th, 2011

    Gaurav :
    i have unity iphone 1.7 but not ios pro! would assetbunder work on basic unity ios ? or we need ios pro licence for that !

  12. #12 by sathya on September 21st, 2011

    thanks, was looking for asset bundle tutorials and found your.
    was very helpful in kick starting my learning.

  13. #13 by Christina on December 7th, 2011

    very nice tutotial, saved me a lot of time digging around trying to figure out stuff.
    A few notes:
    1) rebuilding the unity project for xcode, it looks like you have to choose ‘append’ instead of ‘replace’, otherwise you lose the assetbundle folder reference in the XCode project.
    I’m not sure how safe it is to choose ‘append’, but it does save the hassle of having to redrop the assetbundle folder onto the root of the xcode project everytime you need to build and test on ios.
    Have you found any way to automate or inject the folder reference into the xcode project without the drag+drop?

    2) When i test on IOS with emulation on my mac, or on the actual IOS device itself, the 3 instantiated cubes show up, but they are missing their material assignment when i inspect them in the hierarchy view in unity, so they show up pink.

    The assetbundle build script was able to find the materials and says it added them (by looking at the Debug.Log output). I’m unsure why the material link to the prefab is not preserved. Any ideas?

  14. #14 by Christina on December 7th, 2011

    found a resolution to one issue in my other post.

    I had unloading the bundle, but unity was being resilient and still rendering the instantiated objects I created with the bundle before it was released. Unity will still draw the basic cube type with an undefined material (pink) instead of crashing.

  15. #15 by Fehr on May 11th, 2012

    Thanks Heaps,

    This is exactly what I was looking for, I’ve reposted links to this page on many Unity forum pages discussing this issue.

    Huge help, so again, thanks.

  16. #16 by Adam on June 22nd, 2012

    Christina :
    found a resolution to one issue in my other post.
    I had unloading the bundle, but unity was being resilient and still rendering the instantiated objects I created with the bundle before it was released. Unity will still draw the basic cube type with an undefined material (pink) instead of crashing.

    A little confused by this post.
    I’m having the issue of the cubes showing up pink. I’m not sure if your post is saying that you solved it or that you weren’t able to.

  17. #17 by Neeraj on August 23rd, 2012

    First of all, thanks for the great blog! I believe your asset bundle generation script requires the materials folder to be inside the objects folder, not a sibling to it. It seems your screenshot above reflects this but the directory structure you asked for says otherwise.

    I am having the same problem, where the cubes appear pink and missing their materials, which was supposed to have been included in the asset bundle.

    Please help!

  18. #18 by Alex on September 14th, 2012

    Hi! First of all, great tutorial, I’ve been trying for days to get assetbundle working on iphone and I coudn’t seem to make it until I found your tutorial. You’ve saved me!

    I was wondering if anyone found the solution to the asset not loading the material and apearing pink? It’s the only thing I’m missing!

    Again, thanks alot!

  19. #19 by Alex on September 18th, 2012

    Adam :

    Christina :
    found a resolution to one issue in my other post.
    I had unloading the bundle, but unity was being resilient and still rendering the instantiated objects I created with the bundle before it was released. Unity will still draw the basic cube type with an undefined material (pink) instead of crashing.

    A little confused by this post.
    I’m having the issue of the cubes showing up pink. I’m not sure if your post is saying that you solved it or that you weren’t able to.

    What he means is that in the Example script, at the very end you have this line of code:
    http://www.assetBundle.Unload(true);
    if you comment or delete this line, the cubes should now be shown with their respective materials. However, I am unsure of the memory implications in this.

(will not be published)


  1. No trackbacks yet.