Два важных этапа задействованы при работе с AssetBundle’ами. На первом этапе происходит скачивание AssetBundle с сервера или диска. Это происходит с помощью класса WWW. На втором этапе загружаются ассеты из AssetBundle для использования их в приложении. Вот пример C# скрипта:
using UnityEngine;
using System.Collections;
public class BundleLoader : MonoBehaviour{
public string url;
public int version;
public IEnumerator LoadBundle(){
while (!Caching.ready)
yield return null;
using(WWW www = WWW.LoadFromCacheOrDownload(url, version)) {
yield return www;
AssetBundle assetBundle = www.assetBundle;
GameObject gameObject = assetBundle.mainAsset as GameObject;
Instantiate(gameObject);
assetBundle.Unload(false);
}
}
void Start(){
StartCoroutine(LoadBundle());
}
}
Этот скрипт добавлен на GameObject как Component. AssetBundle загружается следующим образом:
Пожалуйста, отметьте, что для простоты предыдущего примера он не делает проверок на безопасность. Пожалуйста, для более полного примера взгляните сюда.
Поскольку создание приложения - это итеративный процесс, вы, скорее всего, будете модифицировать свои ассеты много раз. Это потребует пересоздания AssetBundles после каждого изменения, чтобы вы смогли их тестировать. Хотя и есть возможно загружать AssetBundle’ы в редакторе, это делать не рекомендуется. Вместо этого для тестирования вы должны использовать вспомогательную функцию Resources.LoadAssetAtPath, чтобы избежать необходимости использования и пересоздания AssetBundle’ов. Такая функция позволит вам загрузить ассет так, как будто он загружен из AssetBundle, но при этом пропуская этап построения AssetBundle, и кроме того, ваши ассеты всегда находятся в актуальном состоянии.
Далее приведен пример вспомогательного скрипта, позволяющего вам загружать ассеты в зависимости от того, запущен он в редакторе или нет. Добавьте этот код в С# скрипт с именем AssetBundleLoader.cs:
using UnityEngine;
using System.Collections;
public class AssetBundleLoader {
public Object Obj; // The object retrieved from the AssetBundle
public IEnumerator LoadBundle<T> (string url, int version, string assetName, string assetPath) where T : Object {
Obj = null;
#if UNITY_EDITOR
Obj = Resources.LoadAssetAtPath(assetPath, typeof(T));
if (Obj == null)
Debug.LogError ("Asset not found at path: " + assetPath);
yield break;
#else
WWW download;
if ( Caching.enabled ) {
while (!Caching.ready)
yield return null;
download = WWW.LoadFromCacheOrDownload( url, version );
}
else {
download = new WWW (url);
}
yield return download;
if ( download.error != null ) {
Debug.LogError( download.error );
download.Dispose();
yield break;
}
AssetBundle assetBundle = download.assetBundle;
download.Dispose();
download = null;
if (assetName == "" || assetName == null)
Obj = assetBundle.mainAsset;
else
Obj = assetBundle.Load(assetName, typeof(T));
assetBundle.Unload(false);
#endif
}
}
Теперь мы можем использовать скрипт AssetBundleLoader для загрузки ассетов из AssetBundle, если мы запускаем созданное приложение, или для загрузки ассета непосредственно из папки, если мы запускаем его из редактора:
using UnityEngine;
using System.Collections;
public class ExampleLoadingBundle : MonoBehaviour {
public string url = "http://www.mygame.com/myBundle.unity3d"; // URL where the AssetBundle is
public int version = 1; // The version of the AssetBundle
public string assetName = "MyPrefab"; // Name of the Asset to be loaded from the AssetBundle
public string assetPath; // Path to the Asset in the Project folder
private Object ObjInstance; // Instance of the object
void Start(){
StartCoroutine(Download());
}
IEnumerator Download () {
AssetBundleLoader assetBundleLoader = new AssetBundleLoader ();
yield return StartCoroutine(assetBundleLoader.LoadBundle <GameObject> (url, version, assetName, assetPath));
if (assetBundleLoader.Obj != null)
ObjInstance = Instantiate (assetBundleLoader.Obj);
}
void OnGUI(){
GUILayout.Label (ObjInstance ? ObjInstance.name + " instantiated" : "");
}
}
Предыдущий скрипт следует сохранить в файл с именем ExampleLoadingBundle.cs внутри папки Assets. После установки публичным переменным корректных значений и запуска скрипта, он, используя класс AssetBundleLoader, загрузит ассет. Далее ассет будет создан и отображен с использованием GUI.
Вы можете использовать функцию WWW.LoadFromCacheOrDownload, которая автоматически сохранит ваши AssetBundle’ы на диск. Будьте внимательны, так как Webplayer ограничен размером в 50МБ (общее для всех веб-проигрывателей). Вы можете приобрести отдельную лицензию для вашей игры, если вам необходимо больше пространства для кэша.
Если ваши AssetBundle’ы хранятся в папке StreamingAssets в виде распакованных AssetBundle’ов, вы можете использовать AssetBundle.CreateFromFile для обращения к AssetBundle’у на диске. Если AssetBundle’ы в папке StreamingAssets сжаты, вам придётся использовать WWW.LoadFromCacheOrDownload для создания распакованной копии AssetBundle в кэше.
AssetBundle’ы совместимы для некоторых платформ. Воспользуйтесь следующей таблицей для руководства.
Совместимость платформ для AssetBundle’ов | |||||
Автономная сборка | Веб-проигрыватель | iOS | Android | ||
Редактор | Да | Да | Y* | Y* | |
Автономная сборка | Да | Да | |||
Веб-проигрыватель | Да | Да | |||
iOS | Да | ||||
Android | Да |
К примеру, AssetBundle, созданный при активной целевой платформе “Веб-проигрыватель”, будет совместим с редактором и с автономными сборками. Однако, он не будет совместим с приложениями, собранными под iOS или Andoid.
(*) AssetBundles built for mobile platforms might contain data stored in an optimized, platform-specific format which is incompatible with the platform the Editor is running on. Note that it is safe to assume that published games needs different asset bundles per platform. Specifically shaders are different between platforms.
E.g. GI data is stored in a format optimized for ARM architecture and thus is not loadable on x86 CPUs. Shaders are stored for OpenGLES 2 configuration and are not loadable by the Editor which uses the DX9/11 renderer (but works with OpenGLCore renderer). During development, you are recommended to use Simulation Mode mentioned in AssetBundles and the AssetBundle Manager tutorial to avoid Editor and mobile platforms incompatibility.
При создании AssetBundle’ов ассеты идентифицируются их именами без расширения. К примеру, текстура в папке проекта (“Assets/Textures/myTexture.jpg”) идентифицируется и загружается по имени “myTexture”, если вы используете метод по умолчанию. Вы можете повлиять на это в большей степени, если также передадите ваш собственный массив идентификаторов (строк) для каждого объекта при создании вашего AssetBundle в метод BuildPipeline.BuildAssetBundleExplicitAssetNames.
AssetBundles позволяют вам использовать контент в разных играх. Единственное требование - любой ассет, на который ссылаются GameObject’ы в вашем AssetBundle, должен быть включен в AssetBundle. Чтобы убедиться, что все ссылаемые ассеты включены в AssetBundle, передайте флаг BuildAssetBundleOptions.CollectDependencies во время его создания.
AssetBundle’ы могут содержать структуру, называемую дерево типов, благодаря которой информация о типах ассетов понимается компиляторами разных версий Unity. На настольных платформах дерево типов включено по умолчанию, но может быть исключено путём передачи флага BuildAssetBundleOptions.DisableWriteTypeTree в функцию BuildAssetBundle. Веб-проигрыватели в действительности полагаются на дерево типов, поэтому оно всегда включено (то есть флаг DisableWriteTypeTree игнорируется). Дерево типов никогда не включено для мобильных и консольных AssetBundle’ов, поэтому вам необходимо пересоздавать их каждый раз, когда изменяется формат сериализации. Это может произойти в новой версии Unity (исключая релизы для исправления ошибок). Также это может произойти при добавлении или удалении полей в MonoBehaviour, если они включены в AssetBundle. При загрузке AssetBundle Unity выдаст вам сообщение об ошибке, если AssetBundle должен быть пересоздан.
You can use AssetBundle.LoadAllAssets to retrieve an array containing all objects from the AssetBundle. It is not possible to get a list of the identifiers directly. A common workaround is to keep a separate TextAsset to hold the names of the assets in the AssetBundle.
Suppose Bundle A contains a Prefab which has a material and texture. Bundle B has a scene which has an instance of the same prefab. Bundle B needs to access the material. Unless the material is included in a specific asset bundle it will be included in both bundle A and B.