详谈 Unity3D AssetBundle 资源加载,结合实际项目开发实例
第一次搞资源更新方面,这里只说更新,加载,AssetBundle资源加载,谈谈自己的理解,以及自己在项目中遇到的那些神坑,现在回想一下,真的是自己跪着过来的,说多了,都是泪。
我这边是安卓AssetBundle资源加载。欢迎拍砖。
一.Unity中各个目录
我这里说的是移动平台(安卓举例),读,写。所谓读,就是你出大版本的包之后,这个只读的话,就一辈子就这些东西了,不会改变了,不会有其他资源来覆盖或者增加啦。
可写,就是可以加东西进去呗。可能是自己太笨,一开始没怎么注意这意思。竟然往StreamingAssets去实现资源更新(天啦撸)。
Application.StreamingAssetsPath,
StreamingAssets目录必须在Assets根目录下,该目录下所有资源也会被打包到游戏里,不同于Resources目录,该目录下的资源不会进行压缩,同样是只读不可写的。
这里的只可读,不可写,就是除了出大版本的包(重新下载),这里面的东西永远不会变。
各平台StreamingAssets路径打印:
Win:E:/myProj/Assets/StreamingAssets
Mac : /myProj/Assets/StreamingAssets
Andorid:jar:file:///data/app/com.myCompany.myProj-1/base.apk!/assets
iOS: /var/containers/Application/E5543D66-83F3-476D-8A8F-49D5332B3763/myProj.app/Data/Raw
Application.PersistentDataPath
应用程序安装后才会出现。该目录独特之处在于是可读,可写的,所以我们一般将下载的AssetBundle存放于此。
各平台PersistentDataPath路径打印:
Win:C:/Users/lodypig/Appdata/LocalLow/myCompany/myProj
Mac : /Users/lodypig/Library/Application Support/myCompany/myProj
Andorid:/data/data/com.myCompany.myProj/files
iOS: /var/mobile/Containers/Data/Appliction/A112252D-6B0E-459Z-9D49-CD3EAC6D47D/Documents
Application.DataPath
应用程序目录,即Assets目录。使用Appliction.dataPath访问。只读不可写。
各平台DataPath路径:
Win:E:/myProj/Assets
Mac : /myProj/Assets/
Andorid:/data/app/com.myCompany.myProj-1/base.apk!
iOS: /var/containers/Application/E5543D66-83F3-476D-8A8F-49D5332B3763/myProj.app/Data
综上,也就是说,要实现资源更新,你只有把资源下载到Application.PersistentDataPath目录下才可实现资源更新(增加或者替换),其他目录不可能实现。
二.Unity游戏加载的资源是如何分配
首先你得有一个资源服务器(FTP为例),因为StreamingAssets目录是只读的,我们想要实现热更新,StreamingAssets
目录里面的东西一旦第一个版本打出APK的包之后,这里的东西将永远不会变(只读)。由于PersistentDataPath目录是可读可写的,
所以游戏下载资源都会下载到这里面。这样就实现了资源的热更新。
注解:绿色的代表流动,可以不断可以改变的资源。红色线代表,读取persistent目录没有的情况下,读StreamingAssets目录,所以,是永远不变的资源。(除非你去重新下载一个apk的包,就不是热更了)
三.如何加载本地的资源
首先优先判断PersistentDataPath目录下的资源是否存在,因为服务器上的资源都是下载到这里的,最新的资源通过下载到这里并且覆盖,这里的资源
能保持跟服务器一致。(雨松之前讲的UnityAssetBundle例子就是通过加载服务器上的,那个只是一个小案例,不能每次用哪下载到哪,每次都要下载,
这种方式是很不好用的,就第一次用的时候如果资源与服务器不一致,就下载到本地中,即PersistentDataPath目录。)
因为每个游戏一开始出大版本的时候,都会附带大量资源,就是放在StreamingAssets目录,所以,这里存放大量资源。这样减少下载的次数。
其实,换一种说法,PersistentDataPath完全是给StreamingAssets的补丁目录,我是这样理解的。当然,在项目运用中,都需要优先最先资源判断。
四.遇到的那些神神神.....坑
(1) 不要以为在PC端可以加载的路径,安卓也可以用。
我这边因为涉及到WWW加载,贴出我的。
这里主要是通过www 如何加载PersistentDataPath和StreamingAssets这两个目录。
Application.PersistentDataPath:
/// <summary>
/// 天呐,一个Per目录,还两种方法加载。这真的是最后我找出来最完美的,可以加载的,之前还有N种版本,就不提了,网上有,我是经过实测,Unity5.3.4版本
/// 加载PC上安卓平台,加载PC上Standalone,加载安卓真机(APK包),这三个,都是可以加载的(WWW加载),
/// </summary>
public static string PERSISTENT_PATH_DATABASE //= LGameConfig.LOCAL_URL_PREFIX + Application.persistentDataPath + "/DataBase/";
{
get
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
return "file:///"+ Application.persistentDataPath + "/Test/";
#else
return "file://"+ Application.persistentDataPath + "/Test/";
#endif
}
}
Application.StreamingAssets:
public static string STREAMING_PATH_DATABASE
{
//这样写,因为安卓Unity平台是Application.isMobilePlatform==false, 而宏定义中又 ==UNITY_ANDROID。
//因为我们项目中是需要同时在PC下安卓平台和PC下 Standard平台,哈哈
get
{
if (!Application.isMobilePlatform)
{
return "file://" + Application.dataPath + "/StreamingAssets" + "/DataBase/";
}
else
{
return
#if UNITY_ANDROID
Application.streamingAssetsPath+ "/DataBase/";
#else
"file://" + Application.dataPath + "/StreamingAssets" + "/DataBase/";
#endif
}
}
这个加载地址,真的是精华,可能自己太笨,就是搞这个WWW加载安卓StreamingAssets目录,花了大把时间,因为网上,加载方式真的是尼玛一万种,要喷一下,这些人,不实际打到APK测一下,我MDGB呀,坑的我好惨。
(2)不要以为在PC端可以用的方法,在安卓也可以用。
安卓上跟其他平台不一样,安装后,这些文件实际上是在一个Jar压缩包里,所以不能直接用读取文件的函数去读,而要用WWW方式
读取的代码(假设名为"文件.txt")
(3) 安卓资源路径加载,下载问题,真的是这次做AssetBundle最大的障碍。
(4)通过FTP和CDN下载资源的时候对应的 后缀是不同的。
FTP下载 后面用 "/" ,即可。
CDN下载 后面用 "//" ,即可。
(5)不同的加载方式,加载的路径也是不同的。
具体我就不说了。
(6)记得加 加载文件的后缀名
安卓上跟其他平台不一样,安装后,这些文件实际上是在一个Jar压缩包里,所以不能直接用读取文件的函数去读,而要用WWW方式
1.读取的代码(假设名为"文件.txt")
(7)加载方式,First In PersistentDataPath,Then StreamingAssets
IEnumerator LoadAnouncementText()
{
string strUrl = GetTxtPerststentUrl(anouncementText);
WWW www = new WWW(strUrl);
yield return www;
if (www.error == null)
{
mAnoucementText = ConvertByteToString(www.bytes);
}
else if (www.isDone)
{
string strPerUrl = GetTxtStreamUrl(anouncementText);
www = new WWW(strPerUrl);
yield return www;
if (www.error == null)
{
mAnoucementText = ConvertByteToString(www.bytes);
}
else if (www.isDone)
{
Debug.LogError("下载当前表出错" + www.error.ToString());
}
}
} string GetTxtStreamUrl(string name)
{
return STREAMING_PATH_DATABASE + name + ".txt";
} string GetTxtPerststentUrl(string name)
{
return PERSISTENT_PATH_DATABASE + name + ".txt";
}
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!补充!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
补充:对于上面的路径问题大家可能有些困惑。
贴出最详细全面的路径问题,经过测试,完全没问题(安卓,PC都可以用,实际项目中使用)
string GetScenePath(string fileName)
{
string path =DataUrl.GetFilePersistentUrl(fileName)+".unity3d";
// string path= DataUrl.LOCAL_URL_PREFIX + Application.dataPath + "/StreamingAssets/" + "Scene_Main" + ".unity3d";
//读取Per目录的时候不需要加prefix,但是读取Streaming目录时候需加上prefix
bool isPersistentDataPath = System.IO.File.Exists(path);
if (!isPersistentDataPath)
{
path = DataUrl.GetFileStreamingUrl(fileName)+ ".unity3d";
if (!System.IO.File.Exists(path))
{
Debug.LogError("Per,Stream目录 scene bundle都不存在");
return null;
}
} return DataUrl.LOCAL_URL_PREFIX+ path;
} DataUrl.cs
public static string GetFilePersistentUrl(string path)
{
return Application.persistentDataPath+"/" + path;
} public static string GetFileStreamingUrl(string path)
{
return Application.streamingAssetsPath+"/" + path;
}
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
public static readonly string LOCAL_URL_PREFIX = "file:///";
#else
public static readonly string LOCAL_URL_PREFIX="file://";
#endif
五.AssetBundle 加载方式
(转自:https://blog.uwa4d.com/archives/ABTheory.html)
1.用法
AssetBundle加载资源分为两步,第一步是获取AssetBundle对象;第二步是通过该AssetBundle对象加载需要的资源。
第一步:获取AssetBundle对象(可以分为以下两种)
①先获取WWW对象,再通过WWW.assetbundle来获取AssetBundle对象。
public WWW(string url);
记载bundle文件并获取WWW对象,完成后会在内存中创建较大的WebStream(解压后的内容,通常为原Bundle文件的4~5倍,纹理资源可能更大),因此后续的AssetBundle.load可以直接在内存中进行。
public static WWW LoadFromCacheOrDownload(string url,int version,unit crc = 0);
加载Bundle文件并获取WWW对象,同时将解压形式的Bundle内容存入磁盘中作为缓存(如果该Bundle已经在缓存中,则省去这一步),完成后只会在内存中创建较小的SerializedFile,而后续的AssetBundle.load 需要通过IO从磁盘中的缓存中获取。
综上两种方式,直接使用WWW.AssetBundle获取AssetBundle对象。
②直接获取AssetBundle。
public static AssetBundle LoadFromFile(string path);
通过未压缩的Bundle文件,同步创建AssetBundle对象,这是最快的创建方式。创建完后只会在内存中创建较小的SerializedFile,
而后续的AssetBundle.Load需要通过IO从磁盘中获取。
public static AssetBundleCreateRequest LoadFromMemoryAsync(byte[] binary);
通过Bundle的二进制数据,异步创建AssetBundle对象。完成后会在内存中创建较大的WebStream。调用时,Bundle的解压是异步的。
public static LoadromMemory
上述方式的同步版本.
第二步:从AssetBundle加载资源的常用API
public T LoadAsset<T>(string name) where T: Object
2.Load assetBundle 区别
new WWW vs WWW.LoadFromCacheOrDownLoad
①前者的优势
前者的Load操作在内存中进行,相比后者的IO操作开销更小
不形成缓存文件,而后者则需要额外的磁盘空间存放缓存
②前者的劣势
每次加载都涉及到解压的操作,而后者在第二次加载时就省去了解压的开销
在内存中会有较大的WebStream,而后者在内存中通常只有较小的SerializedFile。
六.内存分析
WebStream:在使用new WWW或LoadFromMemory时产生,内存开销较大
SerializedFile:内存开销通常较小,但是一般磁盘中存储资源,需要IO操作。
建议:
AssetBundle文件的大小不超过1MB,因为在普遍情况下Bundle加载时间与其大小并非呈线性关系,过大的Bundle可能引起较大的加载开销。
由于WWW对象的加载是异步的,因此逐个加载容易出现CPU空闲的情况,此时建议适当同时加载多个对象,以增加CPU使用率,同时
加快加载的完成。
卸载:
场景物体(GameObject):这类物件可通过Destroy函数进行卸载。
资源(包括Prefab):除了Prefab以外,资源文件可以通过三种方式来卸载
1)Resource.UnLoadAsset 卸载指定的资源,CPU开销小
2)Resource.UnLoadUnusedAssets:一次卸载所有未被引用的资源,CPU开销大。
3)AssetBundle.UnLoad(true)在卸载AssetBundle对象时,所有该资源引用的资源也一起卸载,因为该方法容易造成资源丢失,不建议经常使用。unload(false),只卸载该资源。
4)WWW对象,调用对象的Dispose函数或将其置为null即可。
5)WebStream:在卸载WWW对象以及对应的AssetBundle对象后,这部分内存即会被引擎自动卸载。
6)SerializedFile:卸载AssetBundle后,这部分内存会被引擎自动卸载。
注意:
在通过AssetBundle.unload(false)卸载AssetBundle对象后,如果重新创建该对象并加载之前加载过的资源到内存时,会出现冗余,即两份相同的资源。
被脚本的静态变量引用的资源,在调用resource.unloadUnusedAssets,并不会被卸载。
推荐:
①对于需要常驻内存的Bundle文件来说,优先考虑减少内存占用,因此对于存放非Prefab资源(纹理)的Bundle文件,可以考虑使用LoadFromCacheOrDownLoad或LoadFromFile加载,从而避免WebStream常驻内存。对于存放较多Prefab资源的Bundle,则考虑使用WWW加载。因为这类Bundle用WWW.LoadFromCacheOrDownLoad加载时产生的SerializedFile可能会比WWW产生的WebStream更大。
②对于加载完后卸载Bundle文件,分两种情况,优先考虑速度(加载场景时),优先考虑流畅度(游戏进行时)
加载场景的情况下,需要注意的是避免WWW对象的逐个加载导致的CPU空闲,优先考虑使用加载速度较快的LoadFromCacheDownLoad或LoadFromFile。
游戏进行时,需要避免使用同步操作引起卡顿,因此考虑使用WWW配合LoadAsync进行平滑的资源加载。
尽量避免游戏进行时调用Resource.UnLoadUnusedAssets().因为该接口开销较大,容易造成卡顿,可以尝试使用
Resource.UnLoad(obj)来逐个进行卸载,以保证游戏流畅度。
详谈 Unity3D AssetBundle 资源加载,结合实际项目开发实例的更多相关文章
- Unity3D AssetBundles 动态加载游戏资源
AssetBundles are files which you can export from Unity to contain assets of your choice. These files ...
- Unity3D之Mecanim动画系统学习笔记(十):Mecanim动画的资源加载相关
资源加载是必备的知识点,这里就说说Mecanim动画的资源如何打包及加载. 注意,Unity4.x和Unity5.x的AssetBundle打包策略不一样,本笔记是基于Unity4.x的AssetBu ...
- AssetBundle使用心得【资源加载】
0.资源加载方式 静态资源 Asset下所有资源称为静态资源 Resources资源 Resources目录下,通过实例化得到的资源 AssetBundle资源 又称为增量更新资源 1.什么是Asse ...
- Unity -- AssetBundle(本地资源加载和加载依赖关系)
1.本地资源加载 1).建立Editor文件夹 2).建立StreamingAssets文件夹和其Windows的子文件夹 将下方第一个脚本放入Editor 里面 脚本一 资源打包AssetBund ...
- u3d外部资源加载加密
原文地址:http://www.cnblogs.com/88999660/archive/2013/04/10/3011912.html 首先要鄙视下unity3d的文档编写人员极度不负责任,到发帖为 ...
- Unity5 AssetBundle打包加载及服务器加载
Assetbundle为资源包不是资源 打包1:通过脚本指定打包 AssetBundleBuild ab = new AssetBundleBuild ...
- AssetBundle异步加载被中断的问题
刘 刘泰言创建于 1 年前 在使用异步接口 yield return AssetBundle.ASyncLoad的时候,难免会想到:这个异步处理完之前如何Cancel掉这个任务?也就是一个AssetB ...
- 理解WebKit和Chromium: Chromium的多进程资源加载机制
转载请注明原文地址:http://blog.csdn.net/milado_nju ##概述 前面介绍了WebKit中的资源加载机制,其实它只是一个框架性的东西,实际的资源加载依赖于各个WebKit移 ...
- Cocos Creator 资源加载流程剖析【二】——Download部分
Download流程的处理由Downloader这个pipe负责(downloader.js),Downloader提供了各种资源的"下载"方式--即如何获取文件内容,有从网络获取 ...
随机推荐
- Binary Search Tree Iterator leetcode
Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the ro ...
- 基于 Koa平台Node.js开发的KoaHub.js的静态服务器重写和索引代码
koa-static-server Static file serving middleware for koa with directory, rewrite and index support k ...
- 1675: [Usaco2005 Feb]Rigging the Bovine Election 竞选划区(题解第一弹)
1675: [Usaco2005 Feb]Rigging the Bovine Election 竞选划区 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: ...
- 2435: [Noi2011]道路修建
2435: [Noi2011]道路修建 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2188 Solved: 639[Submit][Status ...
- SEO-站内优化规范
类别 要求 实际工作要求 程 序 设 计 1.DIV+CSS布局 2.站内导航连接性良好 面包屑导航,翻页方式使用样式二,文章和产品上一页和下一页 3.图片的ALT属性 在编程时注意写 4.超级链接的 ...
- 机器学习基石 3 Types of Learning
机器学习基石 3 Types of Learning Learning with Different Output Space Learning with Different Data Label L ...
- Markdown轻量级标记语言
1. Markdown是什么? Markdown是一种轻量级标记语言,它以纯文本形式(易读.易写.易更改)编写文档,并最终以HTML格式发布.Markdown也可以理解为将以MARKDOWN语言编写的 ...
- App对接支付宝移动支付功能
前段时间看了下app对接支付宝移动支付的功能,并自己总结了下支付宝移动支付的实现流程 一.申请流程 前提是已有现成的应用. 1. 申请地址 https://b ...
- iPhone 设置铃声
每次设置后,长时间不玩就忘记了,把写成博客:好记性不如烂笔头: 第一步:下载喜欢的音乐: 第二步,通过iTunes 文件 下的资料库 导入刚才下载的歌曲: 第三步:截取喜欢的部分,铃声最好设置在30秒 ...
- MYSQL数据库-修改和删除
删除数据库: $ DROP DATABASE t_name; 重命名一张表: $ RENAME TABLE ori_name TO new_name; $ ALTER TABLE ori_name R ...