AssetBundle依赖关系
原地址:http://www.cnblogs.com/realtimepixels/p/3652086.html
Unity AssetBundle Dependencies
In the last few weeks I’ve spent quite a lot of time with Unity’s Asset Bundle system. Understanding how dependencies were tracked. What determines GC cleanup of assets and understanding why the editor profiler produces particular results has been a bit of a struggle. I’m lucky enough to work for a group that allows me to have access to the Unity source code however so this has probably been slightly less painful than it has been for others going down this same path.
First, I’d like to acknowledge what appears to be the most useful piece of Unity Answers information that I’ve come across, located here. This seems to explain the general mechanics of how to include dependencies using the push and then pop method in an editor script.
For my purposes, I created a test project that contained several prefabs all containing simple GUI elements. These elements all used the same fonts and referenced the same textures. This was to verify that, in the editor profiler and in instruments, assets were in fact being shared.
I’ve included my example editor script below to demonstrate some of the methods I used for packing the bundles. In my case, I am traversing all assets in my prefab folder and treating elements with the word “Global” in their name as shared. If I were to have many dependency bundles instead of the one that I have in this example I would have to sort the order in which I am packing these bundles.

using UnityEngine;
using UnityEditor; using System.IO;
using System.Collections.Generic; public class AssetBundleBuild : MonoBehaviour
{
[MenuItem("AssetBundle/Build Bundles")]
private static void BuildBundles()
{
DirectoryInfo directory = new DirectoryInfo(Application.dataPath + "/Prefabs");
FileInfo[] files = directory.GetFiles("*.prefab", SearchOption.AllDirectories); bool shouldPush; BuildPipeline.PushAssetDependencies();
foreach (FileInfo f in files)
{
shouldPush = !f.Name.Contains("Global");
if (shouldPush)
{
BuildPipeline.PushAssetDependencies();
} // Get each asset path
string path = Application.dataPath + "/Bundles/" + f.Name.Substring(0, f.Name.LastIndexOf(".")) + ".bundle";
string assetPath = f.FullName.Substring(f.FullName.IndexOf("Assets", f.FullName.Length - f.FullName.IndexOf("Assets"))); Object asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
//Debug.Log("Asset to pack " + asset + " , " + asset.name); BuildAssetBundleOptions options =
BuildAssetBundleOptions.DisableWriteTypeTree |
BuildAssetBundleOptions.CollectDependencies |
BuildAssetBundleOptions.CompleteAssets |
BuildAssetBundleOptions.DeterministicAssetBundle; if (!shouldPush)
{
Object[] d = EditorUtility.CollectDependencies(new Object[] { asset }); List<Object> dSource = new List<Object>();
List<string> dNames = new List<string>(); // In this case I'm attempting to manually collect dependencies for tracking purposes
// however this does not always seem to be necessary unless you have complex prefab heirarchies
foreach (Object o in d)
{
if (o != null && !dSource.Contains(o))
{
Debug.Log(" -- d " + o + " , " + o.name + " , " + o.GetType()); dSource.Add(o);
dNames.Add(o.name);
}
} Debug.Log("::BUILDING DEPENDENCY BUNDLE:: " + asset.name + " , " + dSource.Count);
BuildPipeline.BuildAssetBundleExplicitAssetNames(
dSource.ToArray(),
dNames.ToArray(),
path,
options,
EditorUserBuildSettings.activeBuildTarget);
}
else
{
Debug.Log("::NON DEPENDENCY:: " + asset.name);
BuildPipeline.BuildAssetBundleExplicitAssetNames(
new Object[] { asset },
new string[] { asset.name },
path,
options,
EditorUserBuildSettings.activeBuildTarget); if (shouldPush)
{
BuildPipeline.PopAssetDependencies();
}
}
} BuildPipeline.PopAssetDependencies(); Debug.Log("[AssetBundleBuild] Complete.");
}
}

Now, from the standpoint of packing shared dependencies, this seemed to work with most asset types such as textures or prefabs but when I included fonts, while they wouldn’t be duplicated across multiple bundles, I would always see two copies of my fonts. One set would be flagged as being ‘used by scripts and native code’ and the other would only be flagged as ‘used by native code’. After performing numerous tests, I discovered that upon opening the dependency bundle containing the font I was interested in, if there was a copy of the font in the project directory as well, both versions would be loaded into Resources. I haven’t been able to verify whether this happens on device as well but my inclination is that it only occurs in the editor.
The Problem of the Decompression Buffer
Another problem that became visible when working with large quantities of bundles that (based off of conversations with Unity technical representatives and the 4.2.0b3 beta client) may have changed in such a way as to render the problem less dire is that Unity, as of 4.1.5, allocates an 8mb decompression buffer per each asset bundle being opened. If there is an attempt to parallelize these requests, each request will allocate it’s own decompression buffer. This can be rather disconcerting as one can see how the allocation amount (especially on restricted memory devices) can really get a guy down.
Although 4.2.0b3 seems to be reducing the decompression buffer size down to 0.5mb per bundle the problem of parallelization still persists. The only immediate solution for individuals loading any quantity of bundles seems to be amortizing the requests in such a way as to prevent too much overlap. If someone out there has a suggestion to mitigate this problem otherwise please drop me a line mcelroy.jon[at]gmail.com
Please explain BuildPipeline.PushAssetDependencies
I've read the docs on BuildPipeline.PushAssetDependencies and gone through the example code several times and it just doesn't make sense to me. Can someone explain it in more detail and clearer language?
For instance, when you call PushAssetDependencies, what gets pushed, and onto where?
In the example code athttp://unity3d.com/support/documentation/ScriptReference/BuildPipeline.PushAssetDependencies.html what does that first call to PushAssetDependencies do? Does it cause Lerpz.unity3d, explosive.unity3d, and AdditiveScene.unity3d to depend on shared.unity3d? (i.e. if you didn't call PushAssetDependencies then lerpzuv.tif would get duplicated into each asset bundle?)
If so, why is every build after that preceded by a push and followed by a pop? I read the comment that says, "By pushing and popping around the asset bundle, this file will share resources but later asset bundles will not share assets in this resource" but I don't understand that sentence. This file will share which resources? Does share mean "make available" or "take from somewhere else"? How does the push and pop accomplish that?
Are there some unnecessary calls to push and pop in the example?
asked Mar 30 '11 at 05:05 PM
duggulous
148 ● 3 ● 4 ● 11
I'll give a shot at answering this based on my own understanding, though please chime in if I have this wrong in some way...
Push and PopAssetDependencies let you control which asset bundles are dependent on another (ie which assets can be excluded). When you call PushAssetDependencies, it instructs BuildPipeline to exclude assets in the next bundle it builds that were already included in the previous one. Dependent asset bundles have to be built in order and then loaded in order. For PushAssetDependencies to work you have to build all of your asset bundles in one script as there's no way (as far as I know) to build an asset bundle with dependencies to a pre-existing bundle.
Here's an example. Suppose you want to create 2 bundles (Bundle1 and Bundle2) and you want Bundle2 to reference assets in Bundle1, instead of including them twice separately. For the sake of visualization, let's say that there's a huge texture file "humungous.png" used by both bundles, but you really only need to have it included in Bundle1. Here's how you would build the bundles with an editor script:
var mode : int = BuildAssetBundleOptions.CollectDependencies |
BuildAssetBundleOptions.CompleteAssets |
BuildAssetBundleOptions.DeterministicAssetBundle;
var path : String = Application.dataPath+"/Bundle1.assetbundle";
BuildPipeline.PushAssetDependencies();
BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/Prefabs/myObject1.prefab"), null, path, mode, BuildTarget.iPhone);
path = Application.dataPath+"/Bundle2.assetbundle";
BuildPipeline.PushAssetDependencies();
BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/Prefabs/myObject2.prefab"), null, path, mode, BuildTarget.iPhone);
BuildPipeline.PopAssetDependencies();
BuildPipeline.PopAssetDependencies();
Because PushAssetDependencies was called before building Bundle2, the "humungous.png" file would be in Bundle1, and Bundle2 would only store a reference to it.
Even if you don't need to redistribute Bundle1, it needs to be rebuilt in the script to correctly prepare Bundle2's dependencies. Each call to PushAssetDependencies tells the BuildPipeline to exclude the shared dependencies of the previous asset bundle in the one created next. While PopAssetDependencies removes the exclusion. The Push and Pop commands are hierarchical, meaning you can nest dependencies. For example, consider if you had a complex project with a bundle for base assets, then other sets of bundles of shared assets for specific environments. Using Push and Pop you can create 'layers' of dependencies.
The use of DeterministicAssetBundle means that each time asset bundles are created, the same internal IDs for objects will be used. This ensures that when you build bundles, the assets are referenced the same way every time. If you don't use DeterministicAssetBundle, then there's a risk that there will be conflicts with new bundles and previously distributed bundles.
Hopefully that helps some.
We have prepared a tutorial about building asset bundles with our uTomate build tool. This tutorial also covers the asset bundle dependency stack and how Pushing and Popping from it affect what ends up in your asset bundles, so it's probably going to help you understand the PushAssetDependencies and PopAssetDependencies commands even if you don't use uTomate for building:http://docs.ancientlightstudios.com/display/UT/How+to+build+Asset+Bundles+with+uTomate
How to build asset bundles with uTomate
What are Asset Bundles and why would I want to use them?
The simplest way to answer this question is having a look at the Unity manual.
AssetBundles are a collection of assets, packaged for loading at runtime. With Asset Bundles, you can dynamically load and unload new content into your application. AssetBundles can be used to implement post-release DLC. They can also be used to reduce the amount of space on disk used by your game, when first deployed. It can also be used to add new content to an already published game.
Asset Dependencies - The Theory
When you build asset bundles you usually want them to be of minimal size. Therefore you don’t want to include all dependencies of a scene or an asset into each asset bundle, as this will duplicate all the assets and will unnecessarily add to the size of your bundles.
Unity provides a concept called Dependency Stack for this. This concept is not necessarily intuitive (see the Unity manual for the gory details) but it allows for a great deal of flexibility. The basic idea of the dependency stack is that all bundles, which are on the same or higher stack level as previously built bundles, will share assets from previously built bundles. Now that is probably somewhat abstract so let’s use an example for this.
Let’s assume you got a level called "Level1" from which you can access two versions of another level, called "Level 2a" and "Level 2b". Let’s also assume that in Level 1 you need a robot mesh and textures and Levels 2a and 2b require the same robot mesh and texture but additionally require a car model and textures. Now we want to build asset bundles for Levels 1, 2a and 2b. If you simply build asset bundles without using the dependency stack, each bundle will contain all the dependencies of the level, that is, you get three bundles looking like this:

This is very inefficient because it includes the robot model and textures into Levels 2a and 2b even though it is already known since Level 1 has been loaded before Levels 2a and 2b. To change this, you can use the dependency stack. You enable the dependency stack by callingBuildPipeline.PushAssetDependencies before building a bundle. This will create a new level on the dependency stack.
Now each bundle that is built will share it’s dependencies with previously built bundles that are on the same or a lower stack level. Let’s see how the dependency makes the bundles smaller:

As you can see, the Level 2a bundle now no longer contains the robot model and textures but shares them with the Level 1 bundle. Unity will do no magic for you though, so you are responsible for loading these bundles in the correct order in your application. Level 2b get’s even smaller as it shares the car model and textures with Level 2a (as 2a got built before 2b on the same stack level).
Now while this yielded some nice savings in bundle space it isn’t exactly what we need. Using this structure makes Level 2b depend on Level 2a. So you will always have to load Level 2a before you can switch to Level 2b otherwise the car model and textures will be missing. Since you can go to either Level 2a or 2b from Level 1 this setup is not useable for our example.
Basically we need the car model and texture in both bundles, the one for Level 2a and the one for Level 2b. To do this, you can use BuildPipeline.PushAssetDependencies once before you build the bundle for Level 2a. This will move this onto a higher dependency stack level. It will still share resources from the Level 1 bundle (as this is on a lower stack level). After you have built the bundle for Level 2a, you call BuildPipeline.PopAssetDependencies. This will remove the higher dependency stack level and therefore future bundles will not share resources with the Level 2a bundle (remember, bundles only share dependencies with bundles on the same or a lower stack level).

This approach works for streamed scene asset bundles as well as for simple asset bundles and players.
How to do it with uTomate
Usually when you want to build asset bundles there is no way around scripting this. Unity provides a simple UI for building asset bundles which lets you select a few files and build an asset bundle from the selection. But this is not sufficient in two ways. First, if you happen to forget to select a certain file that should be in your asset bundle you will notice that only when you test your game online - which less than optimal. Second, the Unity UI does not support modifying the dependency stack, so building asset bundles that depend on each other is not possible using the UI. So scripting this is the way to go (and even the Unity documentation recommends that you build a function for creating your asset bundles).
With uTomate you can do all of this without scripting with the familiar Unity inspector UI. uTomate provides two actions that can interact with asset bundles - the obvious link:/utomate/documentation/actiondocs/build_asset_bundle .html[Build Asset Bundle] action and the Build Player action. Both actions provide options to modify the dependency stack:

Basically if you tick Push Dependencies uTomate will put a new level on the dependency stack (by calling PushAssetDependencies). When you tick Pop Dependencies uTomate will drop a level from the dependency stack (by calling PopAssetDependencies). The Pop All Dependencies flag will clear the dependency stack.
So to build the asset bundles from the example, we need three actions that build the asset bundle for the Levels 1, 2a and 2b. Let’s start with the action for Level1:

Its building an asset bundle for the full Level1 scene and all it’s dependencies. To make sure that the dependency stack is opened, we have ticked the Push Dependencies flag. Now lets continue with the bundle for Level2a:

To make sure that everything that is included in this bundle will not be skipped by the Level2bbundle (since we want both bundles to be independent of each other) we open a new level on the dependency stack by ticking Push Dependencies. We also tick Pop Dependencies, to make sure that new level on the dependency stack is cleared once we finish building the bundle. Apart from this, the setup is basically the same as for Level1, except that we use a different scene here. Finally the action for Level2b:

Here we tick Pop Dependencies to clean up the dependency stack. This is just good practice to ensure that other actions that might come afterwards don’t accidentally reference to asset bundles to which they should not reference. The rest of the setup is the same as for Level2a, except again we use a different scene here.
Now that our actions are set up, all we need to do is putting them into an automation plan:

| I have added a few notes and arranged the actions to visualize the dependency stack levels. This is just a visual help to show how the plan is supposed to work, it is not required to format it this way to make it work. However having a good documentation on your automation plans makes it easier for everyone on the team to understand how it’s supposed to work (see also our article on building robust automation plans). |
Now with the plan set up, all you need to do is to run the plan and have your asset bundles built. Since you automated this you will get repeatable, consistent results as opposed to the error-prone manual building of asset bundles. Automating this with uTomate instead of scripting also provides a few bonuses:
You don’t need any scripting experience to do it, so every team member can understand and modify the building process.
The automation plan provides a nice visual documentation on what asset bundles you build and how they depend on each other.
If you would like to give us feedback on this tutorial shoot us a mail. We’d also like to hear from you if you would like to see a tutorial about a special topic here.
AssetBundle依赖关系的更多相关文章
- 处理Assetbundle依赖关系时想到的一道题
在处理unit3d的assetbundle依赖关系的时候,想到了一道有趣的题目: 给定一堆数据,例如{A = {1, 3, 4}, B = {3, 4}, C = {5, 6}, D = {6, 7, ...
- 【Unity3D技术文档翻译】第1.4篇 AssetBundle 依赖关系
上一章:[Unity3D技术文档翻译]第1.3篇 创建 AssetBundles 本章原文所在章节:[Unity Manual]→[Working in Unity]→[Advanced Develo ...
- [Unity3d][NGUI]两种思路解决AssetBundle的依赖关系.
接上文. 使用上文中的AssetBundle打包方式生成的文件包括了依赖关系中的文件. 一般的使用中并不会发现什么问题. 可是当配合NGUI的时候,使用dynamicFont时打包AssetBundl ...
- Unity -- AssetBundle(本地资源加载和加载依赖关系)
1.本地资源加载 1).建立Editor文件夹 2).建立StreamingAssets文件夹和其Windows的子文件夹 将下方第一个脚本放入Editor 里面 脚本一 资源打包AssetBund ...
- Unity进阶----AssetBundle_02(加载依赖关系及网络资源)(2018/10/31)
网络资源加载: string path ="file://"+ Application.streamingAssetsPath + "\\windows\\123&quo ...
- AssetBundle依赖
[Managing asset dependencies] 一个Asset会依赖其它Asset.可以把一个Asset所依赖的Asset也打包进自己的AssetBundle.可是多个Asset可能依赖同 ...
- Unity5-ABSystem(四):AssetBundle依赖
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/lodypig/article/detai ...
- Ambari服务依赖关系图生成脚本
1. 生成服务依赖关系 #!/usr/bin/python import sys import commands import json def genDependString(ip): url=&q ...
- 解决安装rpm包依赖关系的烦恼 - yum工具介绍及本地源配置方法
版权声明:本文发布于http://www.cnblogs.com/yumiko/,版权由Yumiko_sunny所有,欢迎转载.转载时,请在文章明显位置注明原文链接.若在未经作者同意的情况下,将本文内 ...
随机推荐
- Oracle并行事务回滚相关参数及视图
/******相关参数****/fast_start_parallel_rollback1.取值有3种:false,low,high2.各值含义:false ---禁用并行回滚功能 ...
- STM32F0xx_FLASH编程(片内)配置详细过程
Ⅰ.概述 关于数据的储存,我觉得编程的人基本上都会使用到,只是看你储存在哪里.STM32的芯片内部FLASH都是可以进行编程的,也就是说可以拿来储存数据.但是,很多做一些小应用程序开发的人都没有利用好 ...
- 深入理解Java String#intern() 内存模型
原文出处: codelog.me 大家知道,Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,就会在字符串常量池中创建该字符串然后再返回. 字符串 ...
- Ztack学习笔记(6)-广播组播点播
Zigbee网络中进行数据通信主要有三种类型:单播.组播.广播.那这三种方式如何设置呢,在哪里设置呢, 一. 广播 当应用程序需要将数据包发送给网络的每一个设备时,使用这种模式.广播的短地址有三种 0 ...
- AlertDialog.Builder对话框类的用法
1.在测试时,如何实现一个提示 可以使用 Toast.makeText(this, "这是一个提示", Toast.LENGTH_SHORT).show(); //从资源文件str ...
- 调用微信退款接口时出现System.Security.Cryptography.CryptographicException: 出现了内部错误 解决办法
我总结了一下出现证书无法加载的原因有以下三个 1.证书密码不正确,微信证书密码就是商户号 解决办法:请检查证书密码是不是和商户号一致 2.IIS设置错误,未加载用户配置文件 解决办法:找到网站使用的应 ...
- Android-简单的sdcard文件浏览
功能:能够浏览手机里面的文件夹和文件,代码灰常简单 先看布局 <LinearLayout xmlns:android="http://schemas.android.com/apk/r ...
- golang反射初试
golang反射来自Go AST(Abstract Syntax Tree). reflect操作更多像traverse AST. t := reflect.TypeOf(obj) 使用TypeOf( ...
- 从零开始学ios开发(一):准备起航
首先介绍一下自己的背景,本人09年研究生毕业,大学就不介绍了,反正是上海的一所211大学,学的是计算机科学与技术专业,学生时代,从事过ACM,没有什么太大的成就,中国的牛人是在太多,我的水平,估计连高 ...
- 自学asp.net mvc(一)
之前一直做asp.net的webform,现在想学习一下mvc,把自学中遇到的问题记录到博客. 一.codeplex上托管代码 1. 2. 3. 4. 5. 6. 7. 8. 9.