游戏热更新前的准备

要用的数据类

public class VersionData
{public string downLoadUrl;public string version;public int versionCode;public List<AssetData> assetDatas;
}public class AssetData
{public string abName;public int len;public string md5;
}

AssetBundle打包工具(ABTools)

using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Unity.Plastic.Newtonsoft.Json;
using UnityEditor;
using UnityEngine;public class ABTools : Editor
{/// <summary>/// 生成AB资源文件/// </summary>[MenuItem("Tools/ABTool")]static void Build(){string outPath = Application.dataPath + "/ABTest/";string path = Application.dataPath + "/Res/";//获取文件路径数组string[] filePaths = Directory.GetFiles(path, ".", SearchOption.AllDirectories);foreach (string file in filePaths){//过滤unity自动生成的文件if (Path.GetExtension(file).Contains("meta")) continue;Debug.Log("----------------------------------");Debug.Log(file);string abName = string.Empty;//将文件路径指向Assetsstring fileName = file.Replace(Application.dataPath, "Assets");//获取AssetBundle文件对象AssetImporter assetImporter = AssetImporter.GetAtPath(fileName);//将路径指向Res文件夹下abName = fileName.Replace("Assets/Res/", string.Empty);//替换斜杠abName = abName.Replace("\\", "/");Debug.Log("replace before::" + abName);//判断文件路径中是否有_Comm,有的话说明要把这些文件打在一个包内if (file.Contains("_Comm")){//将文件名替换掉  留下文件夹名abName = abName.Replace("/" + Path.GetFileName(file), string.Empty);Debug.Log("SSS::" + abName);}else{//将文件扩展名去掉abName = abName.Replace(Path.GetExtension(file), string.Empty);}assetImporter.assetBundleName = abName;Debug.Log(abName);//AB包扩展名assetImporter.assetBundleVariant = "u3d";}//打包BuildPipeline.BuildAssetBundles(outPath, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.StandaloneWindows64);string[] filePathss = Directory.GetFiles(path, ".", SearchOption.AllDirectories);foreach (string file in filePathss){//过滤unity自动生成的文件if (Path.GetExtension(file).Contains("meta")) continue;//将文件路径指向Assetsstring fileName = file.Replace(Application.dataPath, "Assets");//获取AssetBundle文件对象AssetImporter assetImporter = AssetImporter.GetAtPath(fileName);assetImporter.assetBundleName = string.Empty;}//刷新文件界面AssetDatabase.Refresh();}static VersionData versionData = new VersionData();/// <summary>/// 生成资源配置文件/// </summary>[MenuItem("Tools/MakeVersion")]static void MakeVersion(){//下载UrlversionData.downLoadUrl = "http://127.0.0.1/game/ABTest/";//版本号versionData.version = "1.0.0";//用来判断版本的int值versionData.versionCode = 0;if (versionData.assetDatas == null){versionData.assetDatas = new List<AssetData>();}else{versionData.assetDatas.Clear();}string abPath = Application.dataPath + "/ABTest/";//获取文件路径数组string[] filePaths = Directory.GetFiles(abPath, ".", SearchOption.AllDirectories);foreach (var file in filePaths){//判断文件路径是否包含meta或manifestif (Path.GetExtension(file).Contains("meta") || Path.GetExtension(file).Contains("manifest")) continue;//转换斜杠string abName = file.Replace("\\", "/");abName = abName.Replace(abPath, string.Empty);//读取文件长度int len = File.ReadAllBytes(file).Length;//计算文件md5值string md5 = FileMD5(file);AssetData data = new AssetData();data.abName = abName;data.len = len;data.md5 = md5;versionData.assetDatas.Add(data);}//序列化对象string version = JsonConvert.SerializeObject(versionData);//写出到本地File.WriteAllText(abPath + "version.txt", version);AssetDatabase.Refresh();}static StringBuilder sb = new StringBuilder();private static string FileMD5(string filePath){FileStream file = new FileStream(filePath, FileMode.Open);MD5 md5 = new MD5CryptoServiceProvider();byte[] bytes = md5.ComputeHash(file);file.Close();for (int i = 0; i < bytes.Length; i++){sb.Append(bytes[i].ToString("X2"));}return sb.ToString();}
}

游戏AB包管理类(ABManager)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.U2D;public class ABManager : Singleton<ABManager>
{AssetBundleManifest assetBundleManifest;string AbPath = Application.dataPath + "/ABTest/";public ABManager(){//获取所有资源的信息AssetBundle assetBundle = AssetBundle.LoadFromFile(Application.dataPath + "/ABTest/ABTest");//加载ManifestassetBundleManifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");assetBundle.Unload(false);}Dictionary<string, MyAssetData> dicAssets = new Dictionary<string, MyAssetData>();//加载所有资源private T[] LoadAssets<T>(string abName) where T : Object{//获取所有依赖路径string[] dependencies = assetBundleManifest.GetAllDependencies(abName);//加载所有依赖的ab包foreach (var item in dependencies){if(dicAssets.ContainsKey(item)){dicAssets[item].count++;}else{AssetBundle assetBundle1 = AssetBundle.LoadFromFile(AbPath + item);MyAssetData assetData = new MyAssetData(assetBundle1);dicAssets.Add(item, assetData);}}//加载该AB包if(dicAssets.ContainsKey(abName)){dicAssets[abName].count++;}else{AssetBundle assetBundle1 = AssetBundle.LoadFromFile(AbPath + abName);MyAssetData assetData = new MyAssetData(assetBundle1);dicAssets.Add(abName, assetData);}return dicAssets[abName].ab.LoadAllAssets<T>();}/// <summary>/// 卸载AB包/// </summary>/// <param name="abName"></param>public void UnLoadAB(string abName){//获取所有依赖文件路径string[] dependencies = assetBundleManifest.GetAllDependencies(abName);//卸载依赖AB包foreach (var item in dependencies){if (dicAssets.ContainsKey(item)){dicAssets[item].count--;if (dicAssets[item].count <= 0){dicAssets[item].Unload();dicAssets.Remove(item);}}}//卸载该AB包if (dicAssets.ContainsKey(abName)){dicAssets[abName].count--;if (dicAssets[abName].count <= 0){dicAssets[abName].Unload();dicAssets.Remove(abName);}}}/// <summary>/// 加载其余资源/// </summary>/// <typeparam name="T"></typeparam>/// <param name="abName"></param>/// <param name="assetName"></param>/// <returns></returns>public T LoadOtherAsset<T>(string abName, string assetName)where T : Object{Object[] objects = LoadAssets<T>(abName);Object obj = null;//遍历AB包内资源foreach (var item in objects){if(item.name.Equals(assetName)){obj = item;break;}}return obj as T;}Dictionary<int, string> dicGameObject = new Dictionary<int, string>();/// <summary>/// 加载GameObject/// </summary>/// <param name="abName"></param>/// <param name="assetName"></param>/// <returns></returns>public GameObject LoadGameObject(string abName, string assetName){Object obj = LoadOtherAsset<GameObject>(abName, assetName);GameObject go = GameObject.Instantiate(obj) as GameObject;dicGameObject.Add(go.GetInstanceID(), abName);return go;}/// <summary>/// 加载图集/// </summary>/// <param name="abName"></param>/// <param name="assetName"></param>/// <returns></returns>public Sprite[] LoadSprites(string abName, string assetName){SpriteAtlas spriteAtlas = LoadOtherAsset<SpriteAtlas>(abName, assetName);Sprite[] sprites = new Sprite[spriteAtlas.spriteCount];spriteAtlas.GetSprites(sprites);return sprites;}/// <summary>/// 加载精灵/// </summary>/// <param name="abName"></param>/// <param name="assetName"></param>/// <param name="spriteName"></param>/// <returns></returns>public Sprite LoadSprite(string abName, string assetName, string spriteName){SpriteAtlas spriteAtlas = LoadOtherAsset<SpriteAtlas>(abName, assetName);return spriteAtlas.GetSprite(spriteName);}/// <summary>/// 销毁GameObject/// </summary>/// <param name="go"></param>public void DestroyGameObject(GameObject go){int id = go.GetInstanceID();string abName = dicGameObject[id];Object.Destroy(go);dicGameObject.Remove(id);UnLoadAB(abName);}
}
/// <summary>
/// 资源数据类
/// </summary>
public class MyAssetData
{public AssetBundle ab;public int count;public MyAssetData(AssetBundle a){ab = a;count = 1;}public void Unload(){ab.Unload(true);}
}

StreamingAssets路径到PersistentData路径以及检查更新

using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using XLua;public class UpdateLoad : MonoBehaviour
{Action update;LuaEnv luaEnv;Action<string> sceneLoadFinish;/// <summary>/// P路径/// </summary>public static string PPath{get{return Application.persistentDataPath + "/ABTest/";}}/// <summary>/// S路径/// </summary>public static string SPath{get{
#if UNITY_ANDROIDreturn "jar:file://" + Application.dataPath + "!/assets/ABTest";
#elsereturn Application.streamingAssetsPath + "/ABTest/";
#endif}}// Start is called before the first frame updatevoid Start(){//判断是否是第一次打开游戏if(!Directory.Exists(PPath)){//创建P路径Directory.CreateDirectory(PPath);//将S文件夹中的文件复制到P路径下StartCoroutine(Copy());}else{//检查是否有更新StartCoroutine(CheckUpdate());}}IEnumerator Copy(){//定义版本文件路径string streamingAssetPathVersion = SPath + "version.txt";Debug.Log(streamingAssetPathVersion);string versionContent = "";
#if UNITY_ANDROID//获取本地版本文件UnityWebRequest unityWebRequest = UnityWebRequest.Get(streamingAssetPathVersion);yield return unityWebRequest.SendWebRequest();if(unityWebRequest.result == UnityWebRequest.Result.ConnectionError){Debug.Log(unityWebRequest.error);}else{versionContent = unityWebRequest.downloadHandler.text;}unityWebRequest.Dispose();
#elseversionContent = File.ReadAllText(streamingAssetPathVersion);#endif//反序列化版本信息VersionData versionData = JsonConvert.DeserializeObject<VersionData>(versionContent);//遍历资源包资源for (int i = 0; i < versionData.assetDatas.Count; i++){AssetData assetData = versionData.assetDatas[i];string spath = SPath + assetData.abName;string p = PPath + assetData.abName;string fileName = Path.GetFileName(spath);string dir = Path.GetDirectoryName(p);if(!Directory.Exists (dir)){Directory.CreateDirectory (dir);}
#if UNITY_ANDROID//从S路径获取文件UnityWebRequest unityWebRequest1 = UnityWebRequest.Get("file://" + spath);yield return unityWebRequest1.SendWebRequest();if(unityWebRequest1.result == UnityWebRequest.Result.ConnectionError){Debug.Log(unityWebRequest1.error);}else{//将文件写入到P路径下File.WriteAllBytes(p, unityWebRequest1.downloadHandler.data);}unityWebRequest1.Dispose();
#elseFile.Copy(spath, dir + fileName);
#endif}//将版本信息也复制到P路径下File.WriteAllText(PPath + "/version.txt", versionContent);yield return null;//检查是否有更新StartCoroutine(CheckUpdate());}IEnumerator CheckUpdate(){string localVersion = PPath + "version.txt";Debug.Log(localVersion);//读取本地版本文件string localVersionContent = File.ReadAllText(localVersion);VersionData localVersionData = JsonConvert.DeserializeObject<VersionData>(localVersionContent);Dictionary<string, AssetData> versionDic = new Dictionary<string, AssetData>();//将本地资源添加到字典中for(int i = 0; i < localVersionData.assetDatas.Count; i++){AssetData assetData = localVersionData.assetDatas[i];versionDic.Add(assetData.abName, assetData);}string remoteVersion = localVersionData.downLoadUrl + "version.txt";string remoteVersioonContent = "";//获取服务器的版本文件UnityWebRequest unityWebRequest = UnityWebRequest.Get(remoteVersion);yield return unityWebRequest.SendWebRequest();if(unityWebRequest.result == UnityWebRequest.Result.ConnectionError){Debug.Log("出错");}else{remoteVersioonContent = unityWebRequest.downloadHandler.text;}//反序列化从服务器中获取的版本文件VersionData remoteVersionData = JsonConvert.DeserializeObject<VersionData>(remoteVersioonContent);List<AssetData> updateList = new List<AssetData>();//先判断本地版本是否小于服务器的版本if(localVersionData.versionCode < remoteVersionData.versionCode){//循环从服务器获取的资源列表for (int i = 0; i < remoteVersionData.assetDatas.Count; i++){AssetData assetData = remoteVersionData.assetDatas[i];//对比本地资源的字典中是否有该资源if(versionDic.ContainsKey(assetData.abName)){//如果有再判断文件md5值是否有变化if(versionDic[assetData.abName].md5 != assetData.md5){updateList.Add(assetData);}}//本地没有该资源直接添加到要更新的资源列表中else{updateList.Add(assetData);}}}else{EnterGame();yield break;}//最后将需要更新的文件从服务器下载到本地for (int i = 0; i < updateList.Count; i++){string abName = updateList[i].abName;UnityWebRequest updateAsset = UnityWebRequest.Get(remoteVersionData.downLoadUrl + abName);yield return updateAsset.SendWebRequest();if(updateAsset.result == UnityWebRequest.Result.ConnectionError){Debug.Log(updateAsset.result);}else{string perPath = PPath + abName;string filename = Path.GetFileName(perPath);string dir = Path.GetDirectoryName(perPath).Replace("\\", "/") + "/";//判断本地是否存在该路径的文件夹if(!Directory.Exists(dir)){Directory.CreateDirectory(dir);}//将下载的资源写入到本地File.WriteAllBytes(dir + filename, updateAsset.downloadHandler.data);}}//最后更新版本文件File.WriteAllText(PPath + "version.txt", remoteVersioonContent);yield return null;EnterGame();}private void EnterGame(){PlayerConfig.Ins.Init();luaEnv = new LuaEnv();luaEnv.AddBuildin("rapidjson", XLua.LuaDLL.Lua.LoadRapidJson);luaEnv.AddLoader(CustomLoader);luaEnv.DoString("require 'GameMain'");Action action = luaEnv.Global.Get<Action>("Login");action();sceneLoadFinish = luaEnv.Global.Get<Action<string>>("SceneLoadFinish");SceneManager.sceneLoaded += sceneLoad;}private void sceneLoad(Scene arg0, LoadSceneMode arg1){sceneLoadFinish(arg0.name);}private byte[] CustomLoader(ref string filepath){string path = Application.dataPath + "/Lua/" + filepath + ".lua";return File.ReadAllBytes(path);}public void Select(){Action action = luaEnv.Global.Get<Action>("Select");action();}// Update is called once per framepublic void Update(){update?.Invoke();}
}

AssetBundle打包以及游戏更新相关推荐

  1. 游戏资源差异化热更新及加密全攻略(Assetbundle 打包 AssetBundle 加载 AssetBundle 加密)

    游戏热更新资源加密的必要性 unity中资源热更新还是Assetbundle为主,资源使用越来越广泛,ab包里可以包含图片.视频或者脚本,都是游戏的知识财产,如果被破解者或者竞争对手解开,拿到里面的内 ...

  2. 【厚积薄发】Crunch压缩图片的AssetBundle打包

    这是第133篇UWA技术知识分享的推送.今天我们继续为大家精选了若干和开发.优化相关的问题,建议阅读时间10分钟,认真读完必有收获. UWA 问答社区:answer.uwa4d.com UWA QQ群 ...

  3. 【Unity3D】基于AssetBundle实现资源热更新

    1 前言 Unity3D 本地资源一般放在 Resources 目录下,但是 Resouces 文件夹的大小不能超过 2G,使用 AssetBundle 管理资源可以解决 Resources 文件夹受 ...

  4. 一个灵活的AssetBundle打包工具

    尼尔:机械纪元 上周介绍了Unity项目中的资源配置,今天和大家分享一个AssetBundle打包工具.相信从事Unity开发或多或少都了解过AssetBundle,但简单的接口以及众多的细碎问题也给 ...

  5. U3D assetbundle打包

    U3D assetbundle打包 using UnityEngine; using System.Collections; using UnityEditor;//此脚本不一定要放于editor目录 ...

  6. unity5.4.3p2里面的AssetBundle打包流程

    unity5.4.3p2里面的AssetBundle打包流程,相比之前unity4.x的打包简单了许多,Unity4.X中打包的时候需要自己去管理依赖关系,各种BuildPipeline.PushAs ...

  7. Riot工程师:三步让你的游戏更新更快更小

    Posted by Cristian on 2015-10-29 in 人物观点, 游戏策划, 游戏美术 | 暂无评论 分享到:微信新浪微博FacebookQQ空间更多0 [Gamelook专稿,转载 ...

  8. 暗影之枪显示连接服务器失败,暗影之枪传奇进不去怎么办?游戏更新进不去问题详解[多图]...

    暗影之枪传奇有时候会发现进不去游戏,因为这是国外服务器,特别是更新之后,这时候要怎么办呢?下面是友情MT为大家带来的暗影之枪传奇游戏更新进不去问题详解,希望能帮助到大家! 暗影之枪传奇进不去怎么办? ...

  9. ANDROID下面的游戏更新目录

    ANDROID下面的游戏更新目录 更新模块的整体方案终于搞定了,包括launcher的自更新,以及framework,app等代码的更新,均测试通过. 很激动地拿到ANDROID上去测试,一下就傻眼了 ...

最新文章

  1. R语言--查看数据类型+类型判断
  2. html固定且居中布局含footer,如何用一行 CSS 实现 10 种现代布局?
  3. 算法笔记 1 31 chapter4
  4. Boost.MultiIndex 使用 multi_index_container::ctor_args_list 的示例
  5. 【原创】shadowebdict开发日记:基于linux的简明英汉字典(三)
  6. 个人发卡网站源码运营版 内置4套模板
  7. 小米10获取root权限_安卓刷机搞机小能手必备的三款root神器 最后一款你肯定用过...
  8. 动态规划之LCS算法
  9. uniapp AES加密解密
  10. java线程栅栏_java 线程栅栏
  11. Windows程序闪退原因查看方法----事件查看器
  12. 利用python创建自定义的股票指数
  13. nasm做一个简单的操作系统写字本(1)
  14. python-控制键盘鼠标
  15. 4.STM32下载不进程序、程序不运行的可能原因
  16. keras导入VGG16下载太慢解决办法
  17. 【c++复健】双指针应用
  18. 迅为RK3399开发板Qt实时时钟测试
  19. 专利是个人申请好还是公司申请好呢?
  20. RS-169系列产品说明书

热门文章

  1. 优秀工具介绍之——Iconfont 阿里图标库
  2. thymeleaf中img标签,如果有图片显示图片,没有图片显示默认图片
  3. 4个经典的品牌营销活动案例分析,值得借鉴!
  4. 单细胞分析的 Python 包 Scanpy(图文详解)
  5. js中的基本数据类型有哪些?
  6. Multipart Mime part imgFile exceeds max filesize 异常解决
  7. Brocade 交换机微码升级方法大全
  8. excel插入图片自动适应表格大小
  9. js手机号码、电话号码正则表达式
  10. Linux: Wsl ubuntu下安装OhMyZsh