Unity逻辑热更新

声明:原博链接: http://blog.csdn.net/janeky/article/details/17652021只是把自己研究实践的过程记录下来

先直接上代码:两个类,一个检测资源更新,有更新则下载到本地(ResUpdate),一个是逻辑热更新(LogicUpdate)一、资源对比更新并下载。资源是否更新了是通过比对服务器与客户端的 资源版本表: version.txt该文本文档内容为:

Cube.assetbundle,78a6cd06388449d16aea5f6eae395abf
hotab.assetbundle,7ae97ca818017fcb7879c56d6a350676
Sphere.assetbundle,c58c93cffdc73669e10d3f449e1fa29c
version.txt,f864be607802fcc71b622d7ea5383c8f

工具地址:https://github.com/kenro/File_Md5_Generator

public class ResUpdate : MonoBehaviour {
    public static readonly string VERSION_FILE="version.txt";
    public static readonly string LOCAL_RES_URL="file://"+Application.dataPath+"/Res/";
    public static readonly string SERVER_RES_URL = "file:///F:/HotUpdateTest/ab/";
    public static readonly string LOCAL_RES_PATH=Application.dataPath+"/Res/";

    private Dictionary<string,string> LocalResVersion;
    private Dictionary<string,string> ServerResVersion;
    private List<string> NeedDownFiles;
    private bool NeedUpdateLocalVersionFile = false;

    // Use this for initialization
    void Start () {

        LocalResVersion = new Dictionary<string, string> ();
        ServerResVersion = new Dictionary<string, string> ();
        NeedDownFiles = new List<string> ();

        //加载本地version配置
        StartCoroutine(DownLoad(LOCAL_RES_URL + VERSION_FILE,
            delegate(WWW localVersion)
            {
                //保存本地的version
                ParseVersionFile(localVersion.text, LocalResVersion);
                StartCoroutine(DownLoad(SERVER_RES_URL + VERSION_FILE,
                    delegate(WWW serverVersion)
                    {
                        //保存服务端version
                        ParseVersionFile(serverVersion.text, ServerResVersion);
                        //对比本地和服务端版本
                        CompareVersion();
                        //加载需要更新的资源
                        DownLoadRes();
                    }));
            }));
    }  

    /// <summary>
    /// 保存version
    /// </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)
        {
            string[] info = item.Split(new char[] { ‘,‘ });
            if (info!=null&&info.Length==2)
            {
                dict.Add(info[0], info[1]);
            }
        }
    }

    /// <summary>
    /// 对比本地和服务端版本
    /// </summary>
    private void CompareVersion()
    {
        foreach (var version in ServerResVersion)
        {
            string fileName = version.Key;
            string serverMd5 = version.Value;
            //新增的资源
            if (!LocalResVersion.ContainsKey(fileName))
            {
                NeedDownFiles.Add(fileName);
            }
            else
            {
                //需要替换的资源
                string localMd5;
                //使用安全的获取方式,即:获取失败就直接替换资源
                LocalResVersion.TryGetValue(fileName, out localMd5);
                if (!serverMd5.Equals(localMd5))
                {
                    NeedDownFiles.Add(fileName);
                }
            }
            //本次有更新,同时更新本地的version.tex
            NeedUpdateLocalVersionFile = NeedDownFiles.Count > 0;
        }
    }

    /// <summary>
    /// 加载需要更新的资源
    /// </summary>
    private void DownLoadRes()
    {
        if (NeedDownFiles.Count==0)
        {
            UpdateLocalVersionFile();
            return;
        }

        string file = NeedDownFiles[0];
        NeedDownFiles.RemoveAt(0);

        StartCoroutine(DownLoad(SERVER_RES_URL + file, delegate(WWW w)
        {
            //将下载的资源替换本地的资源
            ReplaceLocalRes(file, w.bytes);
            DownLoadRes();
        }));
    }

    /// <summary>
    /// 更新本地的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);
            //这里可能是多余的,Flush本来是在没有Close的时候也会
            //将缓存中的数据写入到文件中
            //但是这里紧接着下面就是Close
            stream.Flush();
            stream.Close();
        }
        // 测试用:显示加载的资源包
        StartCoroutine(Show());
    }

    /// <summary>
    /// 测试用:显示加载的资源包
    /// </summary>
    /// <returns></returns>
    private IEnumerator Show()
    {
        WWW asset = new WWW(LOCAL_RES_URL + "Sphere.assetbundle");
        yield return asset;
        AssetBundle bundle = asset.assetBundle;
        Instantiate(bundle.LoadAsset("Sphere"));
        ///这是U3D没有处理好的一个环节。在WWW加载资源完毕后,对资源进行instantiate后,对其资源进行unload,这时问题就发生 了,instantiate处理渲染需要一定的时间,虽然很短,但也是需要1,2帧的时间。此时进行unload会对资源渲染造成影响,以至于没有贴图或 者等等问题发生。
        //解决办法:
        //自己写个时间等待代码,最好不要用WaitForSeconds,U3D的API,这个东西很撮,恶心死我了。。。
        //我估计它这个类写的有问题,检查了好长时间,最后还是自己写了几行代码来替换这个类,解决了问题。
        //等待个0.5秒到1秒之后再进行Unload。这样就不会出现instantiate渲染中就运行unload的情况了。
        yield return null;
        bundle.Unload(false);
    }

    /// <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>
    /// 加载资源(服务器和本地)
    /// </summary>
    /// <param name="url"></param>
    /// <param name="finishFun"></param>
    /// <returns></returns>
    private IEnumerator DownLoad(string url,HandleFinishDownload finishFun)
    {
        using (WWW www = new WWW(url))
        {
            yield return www;
            if (finishFun != null)
            {
                finishFun(www);
            }
        }
    }

    public delegate void HandleFinishDownload(WWW www);
}

二、逻辑热更新将逻辑代码生成Dll,将其打包成assetbundl前将其后缀改为.bytes,不然会报错
public class LogicUpdate : MonoBehaviour
{
    private static readonly string DLL_URL = "file:///D:/HostServer/Blood/AssetBundle/test.assetbundle";

    void Start()
    {
        StartCoroutine(loadDllScript());
    }

    private IEnumerator loadDllScript()
    {
        WWW www = new WWW(DLL_URL);
        yield return www;
        AssetBundle bundle = www.assetBundle;
        TextAsset asset = bundle.LoadAsset("test", typeof(TextAsset)) as TextAsset;

        System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(asset.bytes);
        Type script1 = assembly.GetType("Script");
        GameObject obj = new GameObject();
        obj.AddComponent(script1);

        //Type script2 = assembly.GetType("Script2");
        //obj.AddComponent(script2);
    }  

 
时间: 2024-11-08 22:52:08

Unity逻辑热更新的相关文章

二、unity游戏热更新专题

第 1 章 : 热更新技术学习介绍 课时1:101-热更新技术学习介绍 11:55 什么是热更新? 举例来说 游戏上线后,玩家下载第一个版本(70M左右或者更大),在运营的过程中,如果需要更换UI显示,或者修改游戏的逻辑,这个时候,如果不使用热更新,就需要重新打包,然后让玩家重新下载(浪费流量和时间,体验不好). 热更新可以在不重新下载客户端的情况下,更新游戏的内容. 热更新一般应用在手机网游上. 为什么C#脚本不可以直接更新? C#是一门编程语言,它运行之前需要进行编译,而这个编译的过程在移动

用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用FairyGUI (一)

我们的热更新脚本在实际使用中,当然也要支持常用的第三方组件,例如这里介绍一个非常实用的第三方UI库:FairyGUI. 什么是FairyGUI 这里照搬FaiyGUI官网的介绍: 重新定义 UI 制作流程,全可视化,零代码,是一款同时适用于程序员.设计师和游戏策划的UI制作工具. 配合FairyGUI-SDK,在各个游戏引擎都能得到高效的渲染表现,独特的FairyBatching技术可自动优化复杂界面的DC数量. 我们这里自然是使用它的Unity组件啦. 我们这篇文章的主旨并非是介绍如何使用Fa

Unity3D逻辑热更新,第二代舒爽解决方案,L#使用简介

热更新 天下武功,无坚不破,唯快不破 热更新就是为了更快的把内容推到用户手中. 之前,我设计了C#Light,经过半年多的持续修补,勉强可用,磕磕绊绊.感谢那些,试过,骂过,用过的朋友,在你们的陪伴下一路走来,也让我更坚定了要把这件事做好的决心.于是就有了C#Light的2.0,L#. 为什么叫L#呢?因为这次是直接加载解析DLL执行,Load,有一个L因为直接执行的东西叫做IL,有一个L因为模拟CLR的工作,有一个L于是,就有了L# https://github.com/lightszero/

用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用原型链和EventTrigger

原型链是JS的必备,作为ECMAScript4,原型链也是支持的. 特别说明,ActionScript3是支持完整的面向对象继承支持的,原型链只在某些非常特殊的情况下使用. 本文旨在介绍如何使用原型链. 任意对象类型都有一个prototype属性,包括导入的Unity的API也有.我们可以和JS一样的对这个prototype进行操作,比如动态的添加成员,添加方法等. 如果您决定使用原型链,那么必须舍弃编译时类型检查.如果使用强类型,则编译器会认为动态添加的成员不存在,而提示编译错误.解决办法为去

Unity代码热更新方案 JSBinding + SharpKit 介绍

目前Unity的代码更新方案有很多,主要以lua为主. JSBinding + SharpKit 是一种新的技术,他做了两件事情: JSBinding将C#导出到JS中,让你可以用JS写代码,就好像 lua 一样 SharpKit(sharpkit.net)将C#代码翻译成JS代码. 因此,他可以让你使用C#编译代码,之后一键转换成JS代码并正常运行.如果你的项目已经使用C#开发并希望能够更新代码,欢迎尝试使用. 本文先做简介,后续本博客将做详细介绍. 完全支持序列化功能.即可以在 Inspec

[Unity XLua]热更新XLua入门(二)-俄罗斯方块实例篇

前言 在xLua没出来之前,开源的lua框架基本都是以界面用Lua开发为主,核心战斗用C#开发,但xLua出来之后主推C#开发,Lua用作HotFix,这里我展示的第一个例子就是基于界面的经典2D小游戏--俄罗斯方块,界面逻辑是用C#写,启动加载逻辑是用lua,后面我会继续第二个同样的Demo,但是以纯Lua为主,这个案例明天更新. 效果图 由于我不会美术,所以这里使用的开源的游戏资源,感谢此作者. 游戏启动 C#启动Lua逻辑 using UnityEngine; using System.C

Unity中热更新的实现方式

(1)使用lua脚本编写游戏的UI或者其他的逻辑,lua是一个精悍小巧的脚本语言,所以跨平台运行解析,而且不需要编译的过程 (2)使用C#light (3)使用C#反射技术(可以调用dll文件中的类,这个方法非常的麻烦,并且IOS平台不支持反射技术) 版权声明:本文为博主原创文章,未经博主允许不得转载.

Unity官方公布热更新方案性能对比

孙广东  2016.3.11 Unity应用的iOS热更新 作者:丁治宇 Unity TechnologiesChina Agenda ?  什么是热更新 ?  为何要热更新 ?  如何在iOS 上对Unity 应用进行热更新 ?  支持Unity iOS 热更新的各种Lua 插件的对比 什么是热更新 ? 广义定义 ? 无需关闭服务器,不停机状态下修复漏洞,更新资源等,重点是更新逻辑代码. ? 狭义定义( iOS热更新) ? 无需将代码重新打包提交至AppStore,即可更新客户端的执行代码,即

Unity官方发布热更新方案性能对照

孙广东  2016.3.11 Unity应用的iOS热更新 作者:丁治宇 Unity TechnologiesChina Agenda ?  什么是热更新 ?  为何要热更新 ?  怎样在iOS 上对Unity 应用进行热更新 ?  支持Unity iOS 热更新的各种Lua 插件的对照 什么是热更新 ? 广义定义 ? 无需关闭server,不停机状态下修复漏洞,更新资源等,重点是更新逻辑代码. ? 狭义定义( iOS热更新) ? 无需将代码又一次打包提交至AppStore,就可以更新clien