unityunity5.3 assetbundlee更新的内容会存在本地吗

这几天一直在研究热更新方案
主要思路是:
1.先将代码打包成dll,然后用unity 打包成assetsbundle,
2.WWW加载进入主程序,
3使用System.Reflection.Assembly来创建程序集,
4.然后通过GetType(className),来获取这个类
5.AddComponent进入主程序,加载的dll就执行起来了。
ExportAssetBundles.cs
//打包工具,该工具是网上找来都。谢谢作者!
public class ExportAssetBundles : MonoBehaviour {
//在Unity编辑器中添加菜单
[MenuItem("Custom Editor/Create AssetBunldes ALL")]
static void ExportResource()
// 打开保存面板,获得用户选择的路径
string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "assetbundle");
if (path.Length != 0)
// 选择的要保存的对象
Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | pleteAssets, BuildTarget.StandaloneWindows);
1234567891011121314151617181920
//打包工具,该工具是网上找来都。谢谢作者!public class ExportAssetBundles : MonoBehaviour {&&&&&//在Unity编辑器中添加菜单&&&&&&[MenuItem("Custom Editor/Create AssetBunldes ALL")]&&&&static void ExportResource()&&&&{&&&&&&&&// 打开保存面板,获得用户选择的路径&&&&&&&&&&string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "assetbundle");&&&&&&&&&if (path.Length != 0)&&&&&&&&{&&&&&&&&&&&&// 选择的要保存的对象&&&&&&&&&&&&&&Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);&&&&&&&&&&&&//打包&&&&&&&&&&&&&&BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.StandaloneWindows);&&&&&&&&&&&&&&}&&&&}&&}
using UnityE
using System.C
using System.R
//代码加载器
public class Index : MonoBehaviour
private WWW
public static WWW uiWWW;
private System.Reflection.A
// Use this for initialization
void Start ()
StartCoroutine(loadScript());
private IEnumerator loadScript()
//加载我的代码资源
www = new WWW("http://localhost/Main.assetbundle");
AssetBundle bundle = www.assetB
TextAsset asset = bundle.Load("Main", typeof(TextAsset)) as TextA
assembly = System.Reflection.Assembly.Load(asset.bytes);
Assembly[] assLis = System.AppDomain.CurrentDomain.GetAssemblies();
System.Type script = assembly.GetType("Main");
gameObject.AddComponent(script);
12345678910111213141516171819202122232425262728293031323334353637
using UnityEngine;using System.Collections;using System.Reflection;&//代码加载器public class Index : MonoBehaviour{&&&&&private WWW www;&&&&&public static WWW uiWWW;&&&&&private System.Reflection.Assembly assembly;& // Use this for initialization void Start () &&&&{&&&&&&&&StartCoroutine(loadScript()); }&&&&&private IEnumerator loadScript()&&&&{&&&&&&&&//加载我的代码资源&&&&&&&&www = new WWW("http://localhost/Main.assetbundle");&&&&&&&&yield return www;&&&&&&&&&AssetBundle bundle = www.assetBundle;&&&&&&&&TextAsset asset = bundle.Load("Main", typeof(TextAsset)) as TextAsset;&&&&&&&&assembly = System.Reflection.Assembly.Load(asset.bytes);&&&&&&&&&Assembly[] assLis = System.AppDomain.CurrentDomain.GetAssemblies();&&&&&&&&&System.Type script = assembly.GetType("Main");&&&&&&&&&gameObject.AddComponent(script);&&&&}}
因为在加载的时候遇见安全沙箱问题,所以我将这个策略文件记录下来,方便下次复制粘贴
crossdomain.xml
&?xml version="1.0"?&
&cross-domain-policy&
&site-control permitted-cross-domain-policies=”master-only” /&
&allow-access-from domain="" /&
&allow-access-from domain="*"/&
&/cross-domain-policy&
<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd
&?xml version="1.0"?&&cross-domain-policy&&site-control permitted-cross-domain-policies=”master-only” /&&allow-access-from domain="" /&&allow-access-from domain="*"/&&/cross-domain-policy&
本地调试程序时解决跨域问题的方法:
Edit-&Project Settings-&Eidtor
刚开始的时候想使用序列化来存储一些数据,但是后来却连一个很简单的类序列化dll里面都没法获得
官方对象序列号
[MenuItem("Custom Editor/WriteSpriteData")]
static void FileWriteSpriteData()
TextAsset textasset = AssetDatabase.LoadAssetAtPath("Assets/Resources/Packer/Packer.txt", typeof(TextAsset)) as TextA
Atlas atlas = ScriptableObject.CreateInstance&Atlas&();
//Json其实是NGUIJson这个类,我只是把他提出来。改了个名字
atlas.mList = Json.LoadSpriteData(textasset as TextAsset);
if (atlas.mList == null)
string path = "Assets/Resources/Packer/Packer.asset";
AssetDatabase.CreateAsset(atlas, path);
//Atlas是一个只有一个mList属性都类 mList = new List&UISpriteData&();
Object o = AssetDatabase.LoadAssetAtPath(path, typeof(Atlas));
Object texture = AssetDatabase.LoadAssetAtPath("Assets/Resources/Packer/Packer.mat", typeof(Material));
Object[] t = {texture};
BuildPipeline.BuildAssetBundle(o, t, "Assets/Resources/Packer/Packer.assetbundle");
//AssetDatabase.DeleteAsset(path);
<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd
[MenuItem("Custom Editor/WriteSpriteData")]&&&&static void FileWriteSpriteData()&&&&{&&&&&&&&TextAsset textasset = AssetDatabase.LoadAssetAtPath("Assets/Resources/Packer/Packer.txt", typeof(TextAsset)) as TextAsset;&&&&&&&&&Atlas atlas = ScriptableObject.CreateInstance&Atlas&();//Json其实是NGUIJson这个类,我只是把他提出来。改了个名字&&&&&&&&atlas.mList = Json.LoadSpriteData(textasset as TextAsset);&&&&&&&&&if (atlas.mList == null)&&&&&&&&&&&&return;&&&&&&&&&string path = "Assets/Resources/Packer/Packer.asset";&&&&&&&&&AssetDatabase.CreateAsset(atlas, path);//Atlas是一个只有一个mList属性都类 mList = new List&UISpriteData&();&&&&&&&&Object o = AssetDatabase.LoadAssetAtPath(path, typeof(Atlas));&&&&&&&&Object texture = AssetDatabase.LoadAssetAtPath("Assets/Resources/Packer/Packer.mat", typeof(Material));&&&&&&&&&Object[] t = {texture};&&&&&&&&&BuildPipeline.BuildAssetBundle(o, t, "Assets/Resources/Packer/Packer.assetbundle");&&&&&&&&&&&&&&&&//AssetDatabase.DeleteAsset(path);&&&&}
这是使用序列化数据的加载方式,在不用反射的情况下,下面代码加载能够成功,但是使用了反射,下面的代码就加载不成功了。这个问题我也很费解,暂时我没办法解决
读取序列化对象
IEnumerator LoadAtlas()
www = new WWW("http://localhost/Packer.assetbundle");
//WoodenAtlas.assetbundle
//Packer.assetbundle
//用来断点都时候看看里面所包含都数据
Object[] os = www.assetBundle.LoadAll();
Material mete = www.assetBundle.Load("Packer", typeof(Material)) as M
Atlas atlas = www.assetBundle.mainAsset as A
GameObject go = new GameObject("UIAtlas");
UIAtlas uiatlas = go.AddComponent&UIAtlas&();
uiatlas.spriteMaterial =
uiatlas.spriteList = atlas.mL
GameObject sprite = new GameObject("Sprite");
UISprite ui = NGUITools.AddChild&UISprite&(sprite);
ui.atlas =
ui.spriteName = "dynamite";
Debug.Log("Load");
www.assetBundle.Unload(false);
www.Dispose();
1234567891011121314151617181920212223242526
IEnumerator LoadAtlas()&&&&{&&&&&&&&www = new WWW("http://localhost/Packer.assetbundle");&&&&&&&&//WoodenAtlas.assetbundle&&&&&&&&//Packer.assetbundle&&&&&&&&yield return www;//用来断点都时候看看里面所包含都数据&&&&&&&&Object[] os = www.assetBundle.LoadAll();&&&&&&&&&Material mete = www.assetBundle.Load("Packer", typeof(Material)) as Material;&&&&&&&&Atlas atlas = www.assetBundle.mainAsset as Atlas;&&&&&&&&GameObject go = new GameObject("UIAtlas");&&&&&&&&UIAtlas uiatlas = go.AddComponent&UIAtlas&();&&&&&&&&uiatlas.spriteMaterial = mete;&&&&&&&&uiatlas.spriteList = atlas.mList;&&&&&&&&&GameObject sprite = new GameObject("Sprite");&&&&&&&&UISprite ui = NGUITools.AddChild&UISprite&(sprite);&&&&&&&&ui.atlas = uiatlas;&&&&&&&&ui.spriteName = "dynamite";&&&&&&&&Debug.Log("Load");&&&&&&&&&www.assetBundle.Unload(false);&&&&&&&&www.Dispose();&&&&&&&&&&&&}
因为要看一下代码的执行效率,所以我寻找到了这个类。感觉还可以。使用josn数据,mat文件创建一个UIAtlas的时间大概是30毫秒左右。
System.Diagnostics.S
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Thread.Sleep(10000);
stopWatch.Stop();
<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd<div class="crayon-num crayon-striped-num" data-line="crayon-57debd<div class="crayon-num" data-line="crayon-57debd
System.Diagnostics.Stopwatch;Stopwatch stopWatch = new Stopwatch();stopWatch.Start();Thread.Sleep(10000);stopWatch.Stop();
我使用没有任何Unity环境以外代码来实现壳的制作(我们暂且将其称为Index,其实他就是上面的Index类,代码少得可怜。)
然后主程序是在另一个Unity项目中(这个项目在发布的时候打包成Main.dll)
Main.dll项目通过上面的Index来加载,然后添加到一个GameObject上,主程序的Awake()方法就会执行(Awake是整个程序的主入口)
这个时候所有的资源加载都会在Main.dll里面完成。
在这个过程中,遇到了一个比较麻烦的问题就是,我打包的一些UIAtlas.prefab文件上的UIAtlas这个类,无法找到。
这让我有一些无法理解,因为NGUI的代码已经打包进入了Main.dll,那么为什么我加载prefab的时候,却找不到UIAtlas这个类呢?
最后我只能动态的制作UIAtlas对象来完成这样工作。
那么这样的话,以后做界面,做任何prefab都不能绑定脚本了。都只能加载到内存中动态AddComponent了。这样界面也得用配置文件了。
不过对于我这种从页游转过来的程序,这到不是问题,我有现成的界面编辑器(我博客里有),直接生成XML在游戏中进行组装了。对于能够热更新来说,这点麻烦,其实应该不算麻烦了。
动态生成UIAtlas后,创建了几个Sprite、Button,基本的功能都已经实现,说明这个解决方案是可行的。接下来我将把这个方案运用到我的项目中,更加全面的去实验一下。
这样设计的优点:
1、对IOS的打包也是比较方便。打包IOS 直接拿Main项目打包就可以了。因为不需要热更新了。把代码打包在本地就行了。
2、打包Android项目的时候发布apk只需要发布Index,项目发布版本和没写代码一样大,想到这里我想吐槽一下,Unity就算不写任何代码,发布一个apk也得有7M左右。这也太大了点吧。我可啥都没做啊。
1、哪位大神能给我说说上面我遇到的那个问题,为什么找不到绑定在prefab上的类呢?这是程序集的问题么?哎,刚转C#的人伤不起。
[&#8230;] 3、这也是最主要的,如果你看过我上一篇文章《Unity代码热更新解决方案测试结果总结》你就会知道我为什么要这么做了,都是泪啊。。 [&#8230;]&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)
昨天的分享揭开AssetBundle庐山真面目(一),我们主要剖析了4.x版本下的AssetBundle管理机制,那5.x版本究竟有些什么变化呢?今天我们来一探究竟。
同时如果你恰有相关疑问,欢迎加入QQ群()讨论,当然也不要忘记关注UWA哦。
AssetBundle打包(5.x)基础
public static
AssetBundleManifest BuildAssetBundles(string
outputPath,
BuildAssetBundleOptions assetBundleOptions
= BuildAssetBundleOptions.None,
BuildTarget targetPlatform
= BuildTarget.WebPlayer);
调用BuildPipeline.BuildAssetBundles,引擎将自动根据资源的assetbundleName属性(以下简称abName)批量打包,自动建立Bundle以及资源之间的依赖关系。
在资源的Inpector界面最下方可设置一个abName,每个abName(包含路径)对应一个Bundle,即abName相同的资源会打在一个Bundle中。如果所依赖的资源设置了不同的abName,则会与之建立依赖关系,避免出现冗余。
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/maxwidth.2048/tc./mmbiz_qpic_cn/29ee5bc33ab7e9fd4565e.jpg"
ALT="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)"
TITLE="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)" />
支持增量式发布,即在资源内容改变并重新打包时,会自动跳过内容未变的Bundle。因此,相比4.x,会极大地缩短更新Bundle的时间。
新打包选项
&除了前文提到的,5.x下默认开启的三个选项(CompleteAssets
,用于保证资源的完备性;CollectDependencies,用于收集资源的依赖项;DeterministicAssetBundle,用于为资源维护固定ID)之外,5.x中新增了以下选项:
&#9679;&ForceRebuildAssetBundle,用于强制重打所有AssetBundle文件;
&#9679;&IgnoreTypeTreeChanges,用于判断AssetBundle更新时,是否忽略TypeTree的变化;
&#9679;&AppendHashToAssetBundleName,用于将Hash值添加在AssetBundle文件名之后,开启这个选项,可以直接通过文件名来判断哪些Bundle的内容进行了更新(4.x下普遍需要通过比较二进制等方法来判断,但在某些情况下即使内容不变重新打包,Bundle的二进制也会变化)。
与4.x不同的是,对于移动平台,5.x下默认会将TypeTree信息写入AssetBundle,因此在移动平台上DisableWriteTypeTree选项也变得有意义了。
Manifest文件在4.x版本中,我们通常需要自行维护配置文件,以记录AssetBundle之间的依赖关系,并供运行时使用。而在5.x版本中,Manifest文件可以免去这一过程。
Variant参数Variant参数能够让AssetBundle方便地进行“多分辨率支持”,相关详解请移步下文。
AssetBundle
打包(5.x)进阶
在新系统中,添加了以下两个实用的新特性,也许能够给开发者带来事半功倍的效果。
&Manifest文件
&在打包后生成的文件夹中,每个Bundle都会对应一个manifest文件,记录了Bundle的一些信息,但这类manifest只在增量式打包时才用到;同时,根目录下还会生成一个同名manifest文件及其对应的Bundle文件,通过该Bundle可以在运行时得到一个AssetbundleManifest对象,而所有的Bundle以及各自依赖的Bundle都可以通过该对象提供的接口进行获取。
Variant参数
&在资源的Inspector界面最下方,除了可以指定abName,在其后方还可以指定Variant。打包时,Variant会作为后缀添加在Bundle名字之后。相同abName,不同variant的Bundle中,资源必须是一一对应的,且他们在Bundle中的ID也是相同的,从而可以起到相互替换的作用。
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/maxwidth.2048/tc./mmbiz_qpic_cn/29ee5bc33ab7e9fd4565e.jpg"
ALT="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)"
TITLE="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)" />当需要为手机和平板上的某个UI界面使用两套分辨率不同的纹理、Shader,以及文字提示时,借助Variant的特性,只需创建两个文件夹,分别放置两套不同的资源,且资源名一一对应,然后给两个文件夹设置相同的abName和不同的variant,再给UI界面设置abName,然后进行打包即可。运行时,先选择合适的依赖包加载,那么后续加载UI界面时,会根据已加载的依赖包,呈现出相对应的版本。
开发者注意事项
&#9679;&abName可通过脚本进行设置和清除,也可以通过构造一个AssetBundleBuild数组来打包。
新机制打包无法指定Assetbundle.mainAsset,因此无法再通过mainAsset来直接获取资源。
开启DisableWriteTypeTree可能造成AssetBundle对Unity版本的兼容问题,但会使Bundle更小,同时也会略微提高加载速度。
&#9679;&Prefab之间不会建立依赖,即如果Prefab-A和Prefab-B引用了同一张纹理,而他们设置了不同的abName,而共享的纹理并未设置abName,那么Prefab-A和Prefab-B可视为分别打包,各自Bundle中都包含共享的纹理。因此在使用UGUI,开启Sprite
Packer时,由于Atlas无法标记abName,在设置UI界面Prefab的abName时就需要注意这个问题。
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/maxwidth.2048/tc./mmbiz_qpic_cn/f9b92af149fe3ba31a102.jpg"
ALT="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)"
TITLE="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)" />
&#9679;&5.x中加入了Shader
stripping功能,在打包时,默认情况下会根据当前场景的Lightmap及Fog设置对资源中的Shader进行代码剥离。这意味着,如果在一个空场景下进行打包,则Bundle中的Shader会失去对Lightmap和Fog的支持,从而出现运行时Lightmap和Fog丢失的情况.而通过将Edit-&Project
Settings-&Graphics下shader
Stripping中的modes改为Manual,并勾选相应的mode即可避免这一问题。
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/maxwidth.2048/tc./mmbiz_qpic_cn/bd32c9c5ffe16361cb71d.jpg"
ALT="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)"
TITLE="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)" />
UWA即将推出AssetBundle资源检测功能,高效检测AssettBundle文件的创建是否合理,包括具体资源信息检索、冗余资源检测等,敬请期待!
拜托向朋友们推荐UWA<img WIDTH="20px" HEIGHT="20px"
ALT="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)"
TITLE="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)" />
<img src="/blog7style/images/common/sg_trans.gif" real_src ="/maxwidth.2048/tc./mmbiz_qpic_cn/476a80446eebc98d91ee13cb169da92e.jpg"
ALT="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)"
TITLE="&#8203;【厚积薄发】揭开Unity&AssetBundle庐山真面目(二)" />
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。Unity(27)
1、什么是AssetBundle
AssetBundle是Unity pro提供的一种用来存储资源的文件&#26684;式,它可以存储任意一种Unity引擎能够识别的资源,如Scene、Mesh、Material、Texture、Audio、noxss等等,同时,AssetBundle也可以包含开发者自定义的二进制文件,只需要将自定义文件的扩展名改为.bytes,Unity就可以把它识别为TextAsset,进而就可以被打包到AssetBundle中。Unity引擎所能识别的资源我们称为Asset,AssetBundle就是Asset的一个集合。
AssetBundle的特点:
压缩(缺省)、动态载入、本地缓存;
2、AssetBundle VS Resource
AssetBundle作为Unity官方推崇的资源更新方案,与传统的Resource差异如下:
a、Resource放在Resources目录下,resources.assets文件,单个文件有2GB限制,首次必须全部下载;
b、AssetBundle创建需要通过Editor脚本创建,支持动态下载,是Unity Web Caching License唯一可以缓存的内容;
3、AssetBundle的适用平台与跨平台性
AssetBundle适用于多种平台,包括网页应用、移动应用、桌面应用等,可以动态更新,但不同平台所使用的AssetBundle并不相同,在创建离线AssetBundle的时候需要通过参数来指定目标平台,相容关系如表所示:
4、AssetBundle的工作流程(与flash加载swf类&#20284;)
a、创建AssetBundle;
b、上传到Server;
c、游戏运行时根据需要下载(或者从本地cache中加载)AssetBundle文件;
d、解析加载Assets;
e、使用完毕后释放;
5、创建AssetBundle
5.1、如何创建AssetBundle
Unity引擎提供了创建AssetBundle的API,通过编译管线BuildPipeline来创建AssetBundle文件,总共有三种方法:
a、BuildPipeline.BuildAssetBundle(mainAsset : Object, assets : Object[], pathName : string, options : BuildAssetBundleOptions = BuildAssetBundleOptions.CollectDependencies | pleteAssets,
targetPlatform : BuildTarget = BuildTarget.WebPlayer) : bool
该API将编辑器中的任意类型的Assets打包成一个AssetBundle,适用于对单个大规模场景的细分;
b、BuildPipeline.BuildStreamedSceneAssetBundle(level : string[], locationPath : string, target : BuildTarget) : String
该API将一个或多个场景中的资源及其所有依赖以流加载的方式打包成AssetBundle,一般适用于多单个或多个场景进行集中打包;
c、BuildPipeline.BuildAssetBundleExplicitAssetNames(assets : Object[], assetNames : string[],pathName : string, options : BuildAssetBundleOptions = BuildAssetBundleOptions.CollectDependencies
| pleteAssets, targetPlatform : BuildTarget = BuildTarget.WebPlayer) : bool
该API功能与a相同,但创建的时候可以为每个Object指定一个自定义的名字。(一般不太常用)
5.2、关于BuildAssetBundleOptions
a、CompleteAssets
使每个Asset自身完备,包含所有的Components;
b、CollectDependencies
包含每个Asset依赖的所有其他Asset;
c、DisableWriteTypeTree
在AssetBundle中不包含类型信息。需要注意的是,如果将AssetBundle发布到web平台上,则不能使用这个选项;
d、DeterministricAssetBundle
使每个Object具有唯一的、不变的HashID,便于后续查找可以用于增量发布AssetBundle;
e、UncompressedAssetBundle
不进行数据压缩。如果使用这个选项,因为没有压缩/解压的过程,AssetBundle发布和加载会更快,但是AssetBundle也会更大,导致下载变慢。
5.3、AssetBundle之间的依赖
如果游戏中的某个资源被多个资源引用(例如游戏中的Material),单独创建AssetBundle会使多个AssetBundle都包含被引用的资源(这里跟flash编译选项中的链接选项有些像),从而导致资源变大,这里可以通过指定AssetBundle之间的依赖关系来减少最终AssetBundle文件的大小(把AssetBundle解耦)。
具体方法是在创建AssetBundle之前调用BuildPipeline.PushAssetDependencies和BuildPipeline.PopAssetDependencies来创建AssetBundle之间的依赖关系,它的用法就是一个栈,后压入栈中的元素依赖栈内的元素。(记得要pop!)
举个例子:
现在游戏内有mat1和mat2两个Material,他们使用了相同的Texture zhuanqiang
不使用依赖
6、远端Server的AssetBundle下载
Unity引擎提供了两种方式从服务器下载AssetBundle文件,分别是缓存机制和非缓存机制。
6.1、缓存机制
通过WWW.LoadFromCacheOrDownload (url : string, version : int)接口来下载AssetBundle,下载后的AssetBundle会自动被保存到Unity引擎的缓存区内,该方法是Unity推荐的AssetBundle下载方式。下载AssetBundle的时候,该接口会先在本地缓存中查找该文件,看其之前是否被下载过,如果下载过,则直接从缓存中加载,如果没有,则从服务器尽享下载。这样做的好处是可以节省AssetBundle文件的下载时间,从而提高游戏资源的载入速度(还可以节省下载流量)。同时开启多个Coroutine进行WWW的LoadFromCacheOrDownload操作(缓存中),经测试开启的WWW现成越多,速度会越快,但是需要考虑时机的机器火平台的承载能力。如果一定要从网上下载资源的话,线程数最好设为5个(别人的经验),很多平台也有自己的限制,例如有的浏览器只能同事加载6个等等。
需要注意的是,Unity提供的默认缓存大小是根据发布平台不同而不同的(可以向Unity购买Caching license支持)。目前对于web player的网页游戏,默认缓存大小为50M;对于PC上的客户端或者?上的移动游戏,默认缓存大小为4GB。
WWW www = WWW.LoadFromCacheOrDownload (Url, 1);
AssetBundle ab = www.assetB
6.2、非缓存机制
通过创建一个WWW实例来对AssetBundle文件下载,下载后的AssetBundle文件将不会进入Unity的缓存区。使用这种方法每次都会从远端服务器下载。
WWW www = new WWW(Url);
AssetBundle ab =
www.assetBundle;
7、载入AssetBundle对象
7.1、通过WWW类方法和属性
直接通过WWW.assetBundle属性来创建AssetBundle,注意:通过WWW加载的AssetBundle在解析Asset之前一定要先调用WWW.assetB
7.2、通过API动态创建
AssetBundle.CreateFromFile接口从磁盘创建一个AssetBundle文件的内存对象,该方法仅支持非压缩&#26684;式的AssetBundle。
7.3通过API动态创建
AssetBundle.CreateFromMemory接口可以从内存数据流创建一个AssetBundle内存对象。主要用于对数据的加解密上。
WWW www = new WWW(url);
byte[] encrypedData =
www.bytes;
byte[] decryptedData = YourDecryptionMethod(encrypedData);//解密函数
AssetBundle ab = AssetBundle.CreateFromMemory(decrypedData);
8、从AssetBundle中加载Assets
8.1、Assets的加载
AssetBundle.Load (name : string) : Object 从bundle中加载名为name的对象;
AssetBundle.Load (name : string, type : Type) : Object 从bundle中加载被指定类型的名为name的对象;
AssetBundle.LoadAsync (name : string, type : Type) : AssetBundleRequest 异步地从bundle中加载被指定类型的名为name的对象(异步加载需要Unity Pro专业版);
AssetBundle.LoadAll (type : Type) : Object[] 加载所有包含在asset bundle中且继承自type的对象;
AssetBundle.LoadAll () : Object[] 加载所有包含在asset bundle中的对象;
AssetBundle.mainAsset 主资源在构建资源boundle时指定(只读),该功能可以方便的找到bundle内的主资源。例如,你也许想有一个角色的预制体并包括所有纹理、材质、网&#26684;和动画文件。但是完全操纵角色的预设体应该是你的mainAsset并且易于被访问;
//开始下载
WWW www = new WWW(url);
//等待下载完成
//获取指定的主资源并实例化
Instantiate();
8.2、AssetBundle中加载Level
Application.LoadLevel 该接口可以通过名字或者索引载入AssetBundle文件中包含的对应场景,当夹在新场景时,所有之前加载的GameObject都会被销毁;
Application.LoadLevelAsync 该接口的作用与Application.LoadLevel相同,不同的是该接口是异步加载,即加载时主线程可以继续执行;
Application.LoadLevelAdditive 该接口不同与Application.LoadLevel的是并不销毁之前加载的GameObject;
Application.LoadLevelAdditiveAsync 该接口的作用与Application.LoadLevelAdditive相同,不同的是该接口是异步加载,即加载时主线程可以继续执行;
WWW www = new WWW(url);
AssetBundle ab =
www.assetB
Application.loadLevel(&Level1&);
9、AssetBundle与内存
内存一直都是开发者关注的一个重点,如果要了解清楚内存的使用情况。
9.1、加载AssetBundle对内存的影响
Unity引擎在使用WWW方法时会分配一系列的内存空间来存放WWW实例对象、WebStream数据。该数据包括原始的AssetBundle数据、解压后的AssetBundle数据以及一个用于解压的Decompression Buffer。一般情况下,Decompression Buffer会在原始的AssetBundle解压完成后自动销毁,但需要注意的是,Unity会自动保留一个Decompression
Buffer,不被系统回收,这样做的好处是不用过于频繁的开辟和销毁解压Buffer,从而在一定程度上降低CPU的消耗。
当把AssetBundle解压到内存后,开发者可以使用WWW.assetBundle属性来获取AssetBundle对象,从而可以得到各种Assets,进而对这些Assets进行加载或者实例化操作。加载过程中,Unity会将AssetBundle中的数据流转变为引擎可以识别的信息类型(纹理、材质、对象等)。加载完成后,开发者可以对其进行进一步的操作,比如对象的实例化、纹理和材质的复制和替换等。
9.2、AssetBundle的卸载
1)AssetBundle.Unload(true) 该接口会强制卸载掉所有AssetBundle中加载的Asset,包括AssetBundle的映射结构、自身对Web Stream的引用以及从AssetBundle创建出来的所有资源,该接口不推荐使用。
2)AssetBundle.Unload(false) 该接口会释放AssetBundle内的序列化数据,但是任何从这个AssetBundle中实例化的物体都将完好。当然,你不能从这个AssetBundle中加载更多物体。
Resources.UnloadUnusedAssets该接口会卸载掉没有使用的Assets,作用范围是整个系统。
3)对于实例化出来的GameObject,可以调用GameObject.Destory()接口来卸载。该接口会延后到一个合理的时机进行处理。
注意:这是没有处理好的一个环节。在WWW加载资源完毕后,对资源进行instantiate后,对其资源进行unload,这时问题就发生了,instantiate处理渲染需要一定的时间,虽然很短,但也是需要1,2帧的时间。此时进行unload会对资源渲染造成影响,以至于没有贴图或
者等等问题发生。解决办法:自己写个时间等待代码,等待个0.5秒到1秒之后再进行Unload。这样就不会出现instantiate渲染中就运行unload的情况了。
10、关于其他
10.1、在AssetBundle中嵌入脚本
AssetBundle中的资源上如果Attach了脚本,打包的时候该脚本是不会被打到AssetBundle中的,其实这里只是保存了一个类&#20284;于指针的关联,如果需要把脚本也动态打到AssetBundle中,还需要做一番工作。
首先,将脚本预先编译成assembly,把assembly保存成.bytes文件,这样Unity会把它识别为TextAsset,就可以将这个TextAsset打包到AssetBundle中了,载入后可以通过反射机制使用该脚本,代码如下:
AssetBundle bundle = WWWW.assetB
TextAsset txt = bundle.load(&MyBinaryAsText&, typeof(TextAsset)) as TextA
byte[] bytes = txt.
var assembly = System.Reflection.Assembly.Load(bytes);
需要注意的是,IOS平台不支持动态载入脚本。
10.2、AssetBundle的版本控制
AssetBundle使用WWW.LoadFromCacheOrDownload(string url, int version, uint crc)加载,其中的第二个参数-version可以用来做版本控制,该参数强制用户从服务器下载一个更高版本的AssetBundle。我们可以通过第三个参数crc来实现AssetBundle的内容校验,当crc不为0的时候,Unity会校验AssetBundle的CRC码,如果不等,则说明文件损坏,Unity会重新下载该文件。对于crc的获取,(老版本没有提供方法,只能通过LoadFromCacheOrDownload传一个错误的crc,从log中获取),新版本在BuildAssetBundle的时候增加了一个out类型的参数,该参数会返回正确的crc码,打包的时候可以记录下来以供后面使用。
10.3、关于Editor和Runtime之间共享资源
1)Unity提供了一种可以公用的类——noxssableObject,适用于描述动态划分场景;通过代码划分场景——&打包多个AssetBundle——&将划分信息记录在ScriptableObject中,并保存至Asset——&载入时先载入划分信息,再根据这个划分信息载入AssetBundle。
2)使用XML文件。
10.4、关于编辑器扩展
可以通过事件触发来执行你的编辑器代码,但是我们需要一些编译器参数来告知编译器何时需要触发该段代码。 [MenuItem(XXX)]声明在一个函数上方,告知编译器给Unity3D编辑器添加一个菜单项,并且当点击该菜单项的时候调用该函数。触发函数里可以编写任何合法的代码,可以是一个资源批处理程序,也可以弹出一个编辑器窗口。代码里可以访问到当前选中的内容(通过Selection类),并据此来确定显示视图。与此类&#20284;,[ContextMenu(&XXX&)]可以向你的上下文菜单中添加一个菜单项。
当你编写了一些Component脚本,当它被附属到某个GameObject时,想在编辑视图即可在Scene视图观察到效果,那么你可以把[ExecuteInEditMode]写在类上方来通知编译器,该类的OnGUI和Update等函数在编辑模式也也会被调用。我们还可以使用[AddComponentMenu(&XXX/XXX&)]来把该脚本关联到Component菜单中,点击相应菜单项即可为GameObject添加该Component脚本。
为了避免不必要的包含,Unity3D的运行时和编辑器类分辨存储在不同的Assemblies里(UnityEngine和UnityEditor)。Editor目录下的脚本会在其它脚本之后进行编译,这方便了你去使用那些运行时的内容。而那些目录下的脚本是不能访问到Editor目录下的内容的。所以,你最好把你的编辑器脚本写在Editor目录下。
10.5、关于差量发布
在5.2中介绍了创建AssetBundle的参数,其中的d选项在选中的时候可以使相同的内容两次发布出来的文件是完全一样的,我们在创建AssetBundle的时候选择上这个参数,那么就可以做差量了。测试数据如下:
没有选择该参数:
选择了该参数后:
10.6、关于项目中应用
项目中使用AssetBundle做开发可以使用宏进行隔离,接口封装尽量采用异步接口,通过引用计数cache机制,确定ab的释放时机。大体流程如下:
a、确定加载ab次数(资源数)
c、成功后根据资源url引用计数减去对应资源数
d、引用计数为0的时候调用AssetBundle的Unload(false)
代码中使用的地方可以通过封装的GetObject获取已经加载的对象,使用完成后可以调用Resources的UnloadUnUsedAssets释放资源。
10.7、关于AssetBundle的粒度控制
AB的粒度越小,差量更新的冗余就会越小,粒度越大,差量更新的冗余就会越大。但是,并不是说粒度越小就越好,粒度小了,(运行时)加载的时候会增加IO次数、解压次数(AB一般选择压缩&#26684;式)和申请内存的次数,导致加载时长变长。因此粒度的控制是一个时间与空间平衡的选择过程。经过实验,大体有一个数据可以用来参考,1M左右的AssetBundle包加载性能最好,冗余也可以接受。
10.8、关于AssetBundle的压缩选择
AssetBundle压缩与不压缩的差异主要有两方面:
a、外存(安装包的大小或者安装后占用磁盘空间的大小)
b、加载方式的选择(能否使用同步方法)
这里如果一些对性能要求特别高,资源又不大的AB可以采用非压缩方式,通过CreateFromFile加载AB包,性能最高又不会产生大量的内存。对于其他的资源文件,建议压缩处理,压缩与非压缩在磁盘占用上会有4倍左右的大小差别,如果都采用非压缩&#26684;式,有可能会导致你的磁盘占用达到一个非常恐怖的量级。
10.9、AssetBundle在外存优化中的应用
安装后的磁盘构成基本上是资源的内存&#20540;(Resources目录下资源),举个粒子,一张真彩色的的图片放到Resources目录下安装后占用的内存为4M(4*)。如果你的游戏是一个2D的,又包含很多的图片资源,这样会使你的安装包在用户机器上安装需要大量的磁盘空间,这里你可以把它们打成AB包放到用户的手机上,这样就磁盘占用就会小很多了。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:3708次
排名:千里之外
转载:42篇
(1)(4)(2)(2)(6)(5)(3)(3)(15)(5)

我要回帖

更多关于 unity3d assetbundle 的文章

 

随机推荐