AssetBundle.LoadFromMemory基本上是无法在手机上用的,因为要多占一份内存,所以大多Unity项目都不进行资源加密。
Unity2017.2提供了一个新的API AssetBundle.LoadFromStream,通过名字就可以知道它是流加载,那么就不会像AssetBundle.LoadFromMemory那样多占一份很大的内存了。
打包Assetbundle的同时生成加密文件的两个文件分别加载它。
myab.unity3d
encypt_myab.unity3d
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[MenuItem("Tools/BuildAB")] static void BuildAB() { FileUtil.DeleteFileOrDirectory(Application.streamingAssetsPath); Directory.CreateDirectory(Application.streamingAssetsPath); var manifest = BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, BuildAssetBundleOptions.ChunkBasedCompression | BuildAssetBundleOptions.ForceRebuildAssetBundle, BuildTarget.iOS); foreach (var name in manifest.GetAllAssetBundles()) { var uniqueSalt = Encoding.UTF8.GetBytes(name); var data = File.ReadAllBytes(Path.Combine(Application.streamingAssetsPath, name)); using (var myStream = new MyStream(Path.Combine(Application.streamingAssetsPath, "encypt_" + name),FileMode.Create)) { myStream.Write(data, 0, data.Length); } } AssetDatabase.Refresh(); } |
这里测试的Assetbundle一共有20M, 使用LZ4压缩格式。
加密和解密我这里随便写个简单的异或 ^ 。后面也可以用一些更好的算法,总之加密可以慢,但是解密一定要快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System.IO; public class MyStream : FileStream { const byte KEY = 64; public MyStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) : base(path, mode, access, share, bufferSize, useAsync) { } public MyStream(string path, FileMode mode) : base(path, mode) { } public override int Read(byte[] array, int offset, int count) { var index = base.Read(array, offset, count); for (int i = 0 ; i < array.Length; i++) { array[i] ^= KEY; } return index; } public override void Write(byte[] array, int offset, int count) { for (int i = 0; i < array.Length; i ++) { array[i] ^= KEY; } base.Write(array, offset, count); } } |
界面上放两个Image 分别加载它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
using System.IO; using UnityEngine; using UnityEngine.UI; public class TestStream : MonoBehaviour { [Header("是否启用Stream加载")] public bool isStream = true; float m_LoadTime ; void Start() { if (isStream) { float t = Time.realtimeSinceStartup; using (var fileStream = new MyStream(Application.streamingAssetsPath + "/encypt_myab.unity3d", FileMode.Open, FileAccess.Read, FileShare.None, 1024 * 4, false)) { var myLoadedAssetBundle = AssetBundle.LoadFromStream(fileStream); m_LoadTime = Time.realtimeSinceStartup - t; GetComponent<Image>().sprite = myLoadedAssetBundle.LoadAsset<Sprite>("1"); myLoadedAssetBundle.Unload(false); } } else { float t = Time.realtimeSinceStartup; var myLoadedAssetBundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/myab.unity3d"); m_LoadTime = Time.realtimeSinceStartup - t; GetComponent<Image>().sprite = myLoadedAssetBundle.LoadAsset<Sprite>("1"); myLoadedAssetBundle.Unload(false); } } private void OnGUI() { if (isStream) { GUILayout.Label(string.Format("<size=50>\nAssetBundle.LoadFromStream :{0} </size>", m_LoadTime)); } else { GUILayout.Label(string.Format("<size=50>AssetBundle.LoadFromFile :{0} </size>", m_LoadTime)); } } } |
如下图所示,在iPhone7上,基本上加载时间差不多。
注意:Android下的streamingAssets目录不能使用,因为android下是放在jar里并不是文件系统。一定要用的话需要拷贝到 Application.persistentDataPath下。
- 本文固定链接: https://www.xuanyusong.com/archives/4607
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!
这个方式有实际在项目中使用的吗,lz4在加载的时候,小文件还好,大文件基本是失败的,加载的内容和实际内容都不相符了
没什么用,光FileStream的生命周期必须比AssetBundle长就说明没办法用这个。
请问为什么喃?
资源太大(大于2G)会报错,大佬有解决办法吗?
这个其实我最近也在想怎么办,安卓的硬性规定。
使用using的方式创建FileStream,如果加载的是LZ4格式的资源,并且大约默认的加载块大小,会导致闪退,建议改改示例代码
具体闪退的情况可以看我这篇文章https://blog.csdn.net/t163361/article/details/109534650
多谢分享。
没的解密了,根本想不到思路
ab加密后,Android下从streamingassets拷贝到persistentDataPath下再解密 时间应该会很费吧。当单ab包有点大的时候。
怎么破?
用texturepacker打包的UI图集,构建asserbundle后, 变得很大, 原来只有几百K到变到 几M到十几M不等
真没必要加密
问下雨松用LoadFromStream有没有试过Unload(true)的卸载方式,我们用Unload(true)并在卸载的时候才释放FileStream会导致Android报Too many open files的错误,如果提前释放流应该在什么地方释放呢
调一次LoadFromStream就会多一个FileStream,还不能释放,资源多了这个是无解的。
我目前能想到的方式就是,用LoadFromFile的offset来加密美术资源,lua等文本不用ab,而是直接读文件,这个可以随意加密。
这种做法会导致序列化文件占用内存变高
在项目里测试了。。Mono暴涨。 byte[] array引起
默认一个byte[]只有32K 循环使用 不过可以改
AssetBundle.LoadFromStream(stream, 0, 64);
我使用了AssetBundle.LoadFromStream(stream, 0, 64*1024); 在uwa 的分析工具中 比较了前后2次mono 显示 UnityEngine.AssetDbundle.loadFromStream –》selfPersistentBytes 64.0kb -》 selfPersistentcounts 1 抱歉 无法截图
我是小白刚用unity 用打包资源函数打包资源会报告一个音频解析错误,可是我把音频删了还是会有这个错误,该怎么解决呢?
这种方式加载速度会不会变慢啊
不好意思刚才没有仔细看文章。
毫秒级的, 可以忽略了, 上面我有测试的截图。。
大佬,这种方式加密方式,支持ChunkBase压缩的AB包吗?个人感觉解密后,ChunkBase压缩的优势就不存在了
前排感谢大佬!
那这个加密怎么才能用AS打开?