1. 程式人生 > >Unity資源熱更

Unity資源熱更

out crypto fig catch 委托 assets ice 配置文件 compare

一、創建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("\n");
            }

            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[] { \n });   

        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("\n");
            }
        }

        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模擬服務器和本地

技術分享 技術分享

Unity資源熱更