• Unity资源热更


    Unity2017

    一、创建Bundle打包工具ExportAssetBundles

    using UnityEditor;
    using UnityEngine;
    
    public class ExportAssetBundles : EditorWindow
    {
        [MenuItem("Assets/Build AssetBundle From Selection")]
        static void ExportResourceRespective()
        { // 打开保存面板,获得用户选择的路径 
            string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "assetbundle");
    
            if (path.Length != 0)
            {
                // 选择的要保存的对象 
                Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
                //打包Android 
               // BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.Android);
                //打包iPhone
                //BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.iOS);
                //打包Window 
                BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.StandaloneWindows);
    
                /*BuildAssetBundle有5个参数
                 * 1 第一个是主资源
                 * 2 第二个是资源数组 这两个参数必须有一个不为null  如果主资源存在于资源数组中是没有任何关系的,如果设置了主资源,可以通过Bundle.mainAsset来直接使用它
                 * 3 第三个参数是路径 一般我们设置为  Application.streamingAssetsPath + Bundle的目标路径和Bundle名称
                 * 4 第四个参数有四个选项 .CollectDependencies会去查找依赖  .CompleteAssets会强制包含整个资源  DeterministicAssetBundle会确保生成唯一ID,在打包依赖时会有用到
                 * 5 第五个参数是目标平台
                 */
            }
        }
    
        [MenuItem("Assets/Save Scene")]
        static void ExportScene()
        {
            // 打开保存面板,获得用户选择的路径 
            string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "unity3d");
    
            if (path.Length != 0)
            {
                // 选择的要保存的对象 
                Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
                string[] scenes = { "Assets/Scenes/scene1.unity" };
                //打包场景Windows
                //BuildPipeline.BuildPlayer(scenes, path, BuildTarget.StandaloneWindows, BuildOptions.BuildAdditionalStreamedScenes | BuildOptions.UncompressedAssetBundle);
                //打包场景Iphone
                BuildPipeline.BuildPlayer(scenes, path, BuildTarget.iOS, BuildOptions.BuildAdditionalStreamedScenes | BuildOptions.UncompressedAssetBundle);
            }
        }
    }

    二、资源更新及读取

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using UnityEngine.SceneManagement;
    
    public class ResUpdate : MonoBehaviour
    {
        public static readonly string VERSION_FILE = "version.txt";                           
        public static readonly string LOCAL_RES_URL = "file:///D:/Output/Temp/Res/Local/";    //www加载路径
        public static readonly string SERVER_RES_URL = "file:///D:/Output/Temp/Res/Server/";
    
        public static readonly string LOCAL_RES_PATH = "D:\Output\Temp\Res\Local\";      //本地文件夹路径
        public static readonly string SERVER_RES_PATH = "D:\Output\Temp\Res\Server\";    //服务器文件夹路径
    
        private Dictionary<string, string> LocalResVersion;      //fileName -> Md5
        private Dictionary<string, string> ServerResVersion;
        private List<string> NeedDownFiles;
        private bool NeedUpdateLocalVersionFile = false;
    
        public delegate void HandleFinishDownload(WWW www);
    
        void Start()
        {
            //初始化 
            CreatConfiga(SERVER_RES_PATH);  //创建服务端配置文件 version.txt
            CreatConfiga(LOCAL_RES_PATH);   //创建客户端配置文件 version.txt 此时文件无内容
    
            ServerResVersion = new Dictionary<string, string>();  //fileName -> Md5
            LocalResVersion = new Dictionary<string, string>();
    
            NeedDownFiles = new List<string>();
    
            ////加载本地version配置 
            StartCoroutine(DownLoad(LOCAL_RES_URL + VERSION_FILE, delegate (WWW localVersion)
            {
                //把本地version.txt内容localVersion.text 保存到LocalResVersion字典  ;version.txt此时文件无内容
                ParseVersionFile(localVersion.text, LocalResVersion);  
    
                //加载服务端version配置 
                StartCoroutine(this.DownLoad(SERVER_RES_URL + VERSION_FILE, delegate (WWW serverVersion)
                {
                    //保存服务端version 
                    ParseVersionFile(serverVersion.text, ServerResVersion);
                    //计算出需要重新加载的资源 
                    CompareVersion();
                    //加载需要更新的资源 
                    DownLoadRes();
                }));
            }));
        }
    
        /// <summary>
        /// 依次加载需要更新的资源到SERVER_RES_PATH
        /// </summary>
        private void DownLoadRes()
        {
            if (NeedDownFiles.Count == 0)
            {
                UpdateLocalVersionFile();
                return;
            }
    
            string file = NeedDownFiles[0];
            NeedDownFiles.RemoveAt(0);
    
            StartCoroutine(this.DownLoad(SERVER_RES_URL + file, delegate (WWW w)
            {
                //将下载的资源替换本地就的资源 
                ReplaceLocalRes(file, w.bytes);
                DownLoadRes();
            }));
        }
    
        /// <summary>
        /// 把需要添加或更新文件写入客户端文件夹
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="data"></param>
        private void ReplaceLocalRes(string fileName, byte[] data)
        {
            string filePath = LOCAL_RES_PATH + fileName;
            FileStream stream = new FileStream(LOCAL_RES_PATH + fileName, FileMode.Create);
            stream.Write(data, 0, data.Length);
            stream.Flush();
            stream.Close();
        }
    
        //显示资源 
        private IEnumerator Show()
        {
            string BundleURL = "file:///D:/Output/Temp/Res/Local/Cube.assetbundle";
            using (WWW asset = new WWW(BundleURL))
            {
                yield return asset;
                AssetBundle bundle = asset.assetBundle;
                Instantiate(bundle.LoadAsset("Cylinder"));
                bundle.Unload(false);
                yield return new WaitForSeconds(5);
            }
        }
    
        private IEnumerator LoadScene()
        {
            string path = LOCAL_RES_PATH + "test.unity3d";
            Debug.Log(path);
            AssetBundle bundle = AssetBundle.LoadFromFile(path);
            string[] scenes = bundle.GetAllScenePaths();
            string scene = Path.GetFileNameWithoutExtension(scenes[0]);
            SceneManager.LoadScene(scene);
            yield return new WaitForSeconds(5);
        }
    
        /// <summary>
        /// NeedDownFiles.Count > 0 更新本地的version配置 ; 于场景中加载资源
        /// </summary>
        private void UpdateLocalVersionFile()
        {
            if (NeedUpdateLocalVersionFile)
            {
                StringBuilder versions = new StringBuilder();
                foreach (var item in ServerResVersion)
                {
                    versions.Append(item.Key).Append(",").Append(item.Value).Append("
    ");
                }
    
                FileStream stream = new FileStream(LOCAL_RES_PATH + "//" + VERSION_FILE, FileMode.Create);
                byte[] data = Encoding.UTF8.GetBytes(versions.ToString());
                stream.Write(data, 0, data.Length);
                stream.Flush();
                stream.Close();
            }
            //加载显示对象 
            StartCoroutine(Show());
            //载入场景
            //StartCoroutine(LoadScene());
        }
    
        /// <summary>
        /// 对比 LocalResVersion和ServerResVersion 字典,更新NeedDownFiles列表
        /// </summary>
        private void CompareVersion()
        {
            //遍历ServerResVersion字典
            foreach (var version in ServerResVersion)
            {
                string fileName = version.Key;
                string serverMd5 = version.Value;
                //新增的资源 
                if (!LocalResVersion.ContainsKey(fileName))
                {
                    NeedDownFiles.Add(fileName);
                }
                else
                {
                    //需要替换的资源 依据Md5码校验是否替换
                    string localMd5;
                    LocalResVersion.TryGetValue(fileName, out localMd5);
                    if (!serverMd5.Equals(localMd5))
                    {
                        NeedDownFiles.Add(fileName);
                    }
                }
            }
            //本次有更新,同时更新本地的version.txt 
            NeedUpdateLocalVersionFile = NeedDownFiles.Count > 0;
        }
        /// <summary>
        /// 把version.txt中版本信息写入字典
        /// </summary>
        /// <param name="content"></param>
        /// <param name="dict"></param>
        private void ParseVersionFile(string content, Dictionary<string, string> dict)
        {
            if (content == null || content.Length == 0)
            {
                return;
            }
            string[] items = content.Split(new char[] { '
    ' });   
    
            foreach (string item in items)   //test.unity3d,dab1894eb803a45b8e8a07b3daac2b17
            {
                string[] info = item.Split(new char[] { ',' });
                if (info != null && info.Length == 2)
                {
                    dict.Add(info[0], info[1]);
                }
            }
    
        }
        /// <summary>
        /// 根据url连接 返回WWW  给委托
        /// </summary>
        /// <param name="url"></param>
        /// <param name="finishFun"></param>
        /// <returns></returns>
        private IEnumerator DownLoad(string url, HandleFinishDownload finishFun)
        {
            WWW www = new WWW(url);
            yield return www;
    
            if (finishFun != null)
            {
                finishFun(www);
            }
            www.Dispose();
        }
    
        /// <summary>
        /// 获取resPath下打包资源,创建配置文件version.txt
        /// </summary>
        /// <param name="resPath"></param>
        public void CreatConfiga(string resPath)
        {
            string[] files = Directory.GetFiles(resPath, "*", SearchOption.AllDirectories);
            System.Text.StringBuilder versions = new System.Text.StringBuilder();
    
            for (int i = 0; i < files.Length; i++)
            {
                string filePath = files[i];
                string extension = filePath.Substring(files[i].LastIndexOf("."));
                if (extension == ".assetbundle" || extension == ".unity3d")
                {
                    string relativePath = filePath.Replace(resPath, "").Replace("\", "/");
                    string md5 = MD5File(filePath);
                    versions.Append(relativePath).Append(",").Append(md5).Append("
    ");
                }
            }
    
            FileStream stream = new FileStream(resPath + "\" + "version.txt", FileMode.Create);
            byte[] data = Encoding.UTF8.GetBytes(versions.ToString());
            stream.Write(data, 0, data.Length);
            stream.Flush();
            stream.Close();
    
        }
        /// <summary>
        /// 根据文件名生成Md5码
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static string MD5File(string file)
        {
            try
            {
                FileStream fs = new FileStream(file, FileMode.Open);
                System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] retVal = md5.ComputeHash(fs);
                fs.Close();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < retVal.Length; i++)
                {
                    sb.Append(retVal[i].ToString("x2"));
                }
                return sb.ToString();
            }
            catch (System.Exception ex)
            {
                throw new System.Exception("md5file() fail,error:" + ex.Message);
            }
        }
        
    }

    三、这里创建了两个文件夹Server&Local模拟服务器和本地

          

    四、网络测试

    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class RuziniuDriver : MonoBehaviour
    {
        public static string LOCAL_RES_PATH;    //local exchange path
        public static readonly string VERSION_FILE = "version.txt";
        public static readonly string SERVER_RES_URL = "http://54.193.6.32:8080/U3dFileToServer/upLoad/WFJ/AssetBundle/";
        string LOCAL_RES_URL = null;
    
        public delegate void HandleFinishDownload(WWW www);
    
        private Dictionary<string, string> LocalResVersion;      //fileName -> Md5
        private Dictionary<string, string> ServerResVersion;
        private List<string> NeedDownFiles;
        private bool NeedUpdateLocalVersionFile = false;
    
        // Use this for initialization
        void Start()
        {
            LOCAL_RES_PATH = Application.persistentDataPath + "/StreamingAssets/";
            
            if(!Directory.Exists(LOCAL_RES_PATH))
            {
                Directory.CreateDirectory(LOCAL_RES_PATH);
            }
    
            CreatConfig(LOCAL_RES_PATH);    //创建客户端
    
            ServerResVersion = new Dictionary<string, string>();  //fileName -> Md5
            LocalResVersion = new Dictionary<string, string>();
            NeedDownFiles = new List<string>();
    
            StartCoroutine(DownLoad(LOCAL_RES_PATH + VERSION_FILE, delegate (WWW localVersion)
            {
                //把本地version.txt内容localVersion.text 保存到LocalResVersion字典  ;version.txt此时文件无内容
                ParseVersionFile(localVersion.text, LocalResVersion);
    
                //加载服务端version配置 
                StartCoroutine(this.DownLoad(SERVER_RES_URL + VERSION_FILE, delegate (WWW serverVersion)
                {
                    //保存服务端version 
                    ParseVersionFile(serverVersion.text, ServerResVersion);
                    //计算出需要重新加载的资源 
                    CompareVersion();
                    //加载需要更新的资源 
                    DownLoadRes();
                }));
            }));
        }
    
        /// <summary>
        /// 对比 LocalResVersion和ServerResVersion 字典,更新NeedDownFiles列表
        /// </summary>
        private void CompareVersion()
        {
            //遍历ServerResVersion字典
            foreach (var version in ServerResVersion)
            {
                string fileName = version.Key;
                string serverMd5 = version.Value;
                //新增的资源 
                if (!LocalResVersion.ContainsKey(fileName))
                {
                    NeedDownFiles.Add(fileName);
                }
                else
                {
                    //需要替换的资源 依据Md5码校验是否替换
                    string localMd5;
                    LocalResVersion.TryGetValue(fileName, out localMd5);
                    if (!serverMd5.Equals(localMd5))
                    {
                        NeedDownFiles.Add(fileName);
                    }
                }
            }
            //本次有更新,同时更新本地的version.txt 
            NeedUpdateLocalVersionFile = NeedDownFiles.Count > 0;
        }
        /// <summary>
        /// 根据VERSION_FILE 依次加载需要更新的资源到LOCAL_RES_PATH
        /// </summary>
        private void DownLoadRes()
        {
            if (NeedDownFiles.Count == 0)
            {
                UpdateLocalVersionFile();
                return;
            }
    
            string file = NeedDownFiles[0];
            NeedDownFiles.RemoveAt(0);
    
            StartCoroutine(this.DownLoad(SERVER_RES_URL + file,
                delegate (WWW w)
                {
                    //将下载的资源替换本地旧的资源 
                    ReplaceLocalRes(file, w.bytes);
                    DownLoadRes();
                })
            );
        }
    
        /// <summary>
        /// 根据url连接 返回WWW  给委托
        /// </summary>
        /// <param name="url"></param>
        /// <param name="finishFun"></param>
        /// <returns></returns>
        private IEnumerator DownLoad(string url, HandleFinishDownload finishFun)
        {
            WWW www = new WWW(url);
            yield return www;
    
            if (finishFun != null)
            {
                finishFun(www);
            }
            www.Dispose();
        }
    
        /// <summary>
        /// 把需要添加或更新文件写入客户端文件夹
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="data"></param>
        private void ReplaceLocalRes(string fileName, byte[] data)
        {
            string filePath = LOCAL_RES_PATH + fileName;
            FileStream stream = new FileStream(LOCAL_RES_PATH + fileName, FileMode.Create);
            stream.Write(data, 0, data.Length);
            stream.Flush();
            stream.Close();
        }
    
        /// <summary>
        /// 更新 VERSION_FILE:version.txt
        /// </summary>
        private void UpdateLocalVersionFile()
        {
            if (NeedUpdateLocalVersionFile)
            {
                StringBuilder versions = new StringBuilder();
                foreach (var item in ServerResVersion)
                {
                    versions.Append(item.Key).Append(",").Append(item.Value).Append("
    ");
                }
    
                FileStream stream = new FileStream(LOCAL_RES_PATH + "//" + VERSION_FILE, FileMode.Create);
                byte[] data = Encoding.UTF8.GetBytes(versions.ToString());
                stream.Write(data, 0, data.Length);
                stream.Flush();
                stream.Close();
            }
    
            //加载显示对象 
            //StartCoroutine(Show());
    
            //载入场景
            //StartCoroutine(LoadScene());
        }
    
        /// <summary>
        /// 把version.txt中版本信息写入字典
        /// </summary>
        /// <param name="content">资源名称</param>
        /// <param name="dict">版本字典</param>
        private void ParseVersionFile(string content, Dictionary<string, string> dict)
        {
            if (content == null || content.Length == 0)
            {
                return;
            }
            string[] items = content.Split(new char[] { '
    ' });
    
            foreach (string item in items)   //test.unity3d,dab1894eb803a45b8e8a07b3daac2b17
            {
                string[] info = item.Split(new char[] { ',' });
                if (info != null && info.Length == 2)
                {
                    dict.Add(info[0], info[1]);
                }
            }
        }
    
        /// <summary>
        /// 获取resPath下打包资源,创建配置文件version.txt
        /// </summary>
        /// <param name="resPath"></param>
        void CreatConfig(string resPath)
        {
            string[] files = Directory.GetFiles(resPath, "*", SearchOption.AllDirectories);
            System.Text.StringBuilder versions = new System.Text.StringBuilder();
    
            for (int i = 0; i < files.Length; i++)
            {
                string filePath = files[i];
                string extension = filePath.Substring(files[i].LastIndexOf("."));
                if (extension == ".assetbundle" || extension == ".unity3d")
                {
                    string assetName = filePath.Replace(resPath, "").Replace("\", "");
                    string md5 = MD5File(filePath);
                    versions.Append(assetName).Append(",").Append(md5).Append("
    ");
                }
            }
    
            FileStream stream = new FileStream(resPath + "\" + "version.txt", FileMode.Create);
            byte[] data = Encoding.UTF8.GetBytes(versions.ToString());
            stream.Write(data, 0, data.Length);
            stream.Flush();
            stream.Close();
        }
        /// <summary>
        /// 根据文件名生成Md5码
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        string MD5File(string file)
        {
            try
            {
                FileStream fs = new FileStream(file, FileMode.Open);
                System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] retVal = md5.ComputeHash(fs);
                fs.Close();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < retVal.Length; i++)
                {
                    sb.Append(retVal[i].ToString("x2"));
                }
                return sb.ToString();
            }
            catch (System.Exception ex)
            {
                throw new System.Exception("md5file() fail,error:" + ex.Message);
            }
        }
    
    }
  • 相关阅读:
    Hadoop杂记
    hadoop主节点(NameNode)备份策略以及恢复方法
    (转)第12章 Shell脚本编程
    Hadoop添加删除节点
    secondarynamenode异常
    (转)Memcached笔记——(一)安装&常规错误&监控
    浅(kou)谈(hu)杜教筛
    Pollard_Rho 算法
    Miller_Rabin 素数判定算法
    zoj分类(包括poj已做的)
  • 原文地址:https://www.cnblogs.com/JimmyCode/p/7287032.html
Copyright © 2020-2023  润新知