unity3d热更新解决方案,使用ulua插件开发的框架。

ulua插件下载地址 www.ulua.org,下面要说的是ulua的开发框架。

首先是 LuaLoader 类,它负责把一个 lua 的 table 加载起来,使此 lua 的 table 像一个 unity 的 component 一样挂在游戏对象上,代码如下:

using LuaInterface;
using System;
using UnityEngine;

public class LuaLoader : MonoBehaviour
{
    public string Name;

    LuaTable m_table;
    LuaFunction m_updateFunc;
    LuaFunction m_fixedUpdateFunc;

    void Awake()
    {
        if (string.IsNullOrEmpty(Name))
            throw new ArgumentException("Name");

        m_table = LuaHelper.GetLuaTable(Name);
        if (m_table == null)
            throw new ArgumentNullException("No table in " + Name);

        // Init lua
        m_table["transform"] = transform;
        m_table["gameObject"] = gameObject;

        // Awake
        CallMethod("Awake");

        //
        m_updateFunc = GetMethod("Update");
        m_fixedUpdateFunc = GetMethod("FixedUpdate");
    }

    void Start()
    {
        CallMethod("Start");
    }

    void Update()
    {
        if (m_updateFunc != null)
            m_updateFunc.Call(Time.deltaTime);
    }

    void FixedUpdate()
    {
        if (m_fixedUpdateFunc != null)
            m_fixedUpdateFunc.Call();
    }

    void OnEnable()
    {
        CallMethod("OnEnable");
    }

    void OnDisable()
    {
        CallMethod("OnDisable");
    }

    void OnDestroy()
    {
        CallMethod("OnDestroy");

        // 释放内存
        m_table["transform"] = null;
        m_table["gameObject"] = null;

        m_table.Release();
        m_table = null;

        if (m_updateFunc != null)
            m_updateFunc.Release();

        if (m_fixedUpdateFunc != null)
            m_fixedUpdateFunc.Release();

        LuaScriptMgr.Instance.LuaGC();
    }

    LuaFunction GetMethod(string methodName)
    {
        return m_table[methodName] as LuaFunction;
    }

    void CallMethod(string name)
    {
        var func = GetMethod(name);

        if (func != null)
        {
            func.Call();
            func.Release();     // 释放内存
        }
    }

    public LuaTable Table
    {
        get { return m_table; }
    }
}

  其次是 lua 与 c# 的交互,提供了两个帮助类,一个是 LuaHelper ,与游戏逻辑无关的方法封装在里面;另外一个是 LuaUtils, lua 中要访问 c# 代码的方法(与游戏逻辑有关的)都封装在里面。

  LuaHelper 关键的几个方法代码如下:

using LuaInterface;
using Resource;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;

public static class LuaHelper
{
    #region GetLuaComponent

    public static LuaTable GetLuaComponent(Transform transform, string type)
    {
        var loaders = transform.GetComponents<LuaLoader>();
        var rightLoader = loaders.FirstOrDefault(lt => lt.Table != null && lt.Table.name == type);

        return rightLoader != null ? rightLoader.Table : null;
    }

    public static LuaTable GetLuaComponent(GameObject gameObject, string type)
    {
        return GetLuaComponent(gameObject.transform, type);
    }

    #endregion

    /// <summary>
    /// 从ab包中加载table,供lua使用
    /// </summary>
    /// <param name="name">table名</param>
    public static void LoadLuaTable(string name)
    {
        if (string.IsNullOrEmpty(name))
            throw new ArgumentException("name");

        LuaTable table = LuaScriptMgr.Instance.GetLuaTable(name);

        if (table == null)
        {
            using (var loadLua = new LoadLuaHandler(name))
                LuaScriptMgr.Instance.DoString(loadLua.Text);
        }
    }

    public static LuaTable GetLuaTable(string name)
    {
        if (string.IsNullOrEmpty(name))
            throw new ArgumentException("name");

        LuaTable table = LuaScriptMgr.Instance.GetLuaTable(name);

        if (table == null)
        {
            using (var loadLua = new LoadLuaHandler(name))
                LuaScriptMgr.Instance.DoString(loadLua.Text);

            table = LuaScriptMgr.Instance.GetLuaTable(name);
        }

        return table;
    }

    public static LuaFunction GetLuaFunction(string className, string funcName)
    {
        if (string.IsNullOrEmpty(className))
            throw new ArgumentException(className);
        if (string.IsNullOrEmpty(funcName))
            throw new ArgumentException(funcName);

        LuaTable table = GetLuaTable(className);
        return table[funcName] as LuaFunction;
    }

    public static object[] CallFunction(string className, string funcName, params object[] args)
    {
        LuaFunction func = GetLuaFunction(className, funcName);
        if (func == null)
            throw new ArgumentNullException(string.Format("Cann‘t find lua function: {0}.{1}", className, funcName));

        var returnArgs = args == null ? func.Call() : func.Call(args);
        func.Release();

        return returnArgs;
    }
}

  另外,关于通过 lua 代码给c#打补丁的功能,是在 UIPanel OnEnable 的第一帧检测补丁和打补丁的,c#代码如下:

using LuaInterface;
using System;
using System.Collections.Generic;
using UnityEngine;

public class LuaPatchManager : IDisposable
{
    List<Patch> m_patches;

    #region Singleton

    static LuaPatchManager s_instance;

    public static LuaPatchManager Instance
    {
        get { return s_instance; }
    }

    #endregion

    #region Patch

    class Patch : IDisposable
    {
        LuaFunction m_validate;
        LuaFunction m_correct;

        public Patch(LuaFunction validate, LuaFunction correct)
        {
            if (validate == null)
                throw new ArgumentNullException("validate");
            if (correct == null)
                throw new ArgumentNullException("correct");

            m_validate = validate;
            m_correct = correct;
        }

        public bool Validate(UIPanel uiPanel)
        {
            var objs = m_validate.Call(uiPanel);
            return (bool)objs[0];
        }

        public void Correct(UIPanel uiPanel)
        {
            m_correct.Call(uiPanel);
        }

        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        void Dispose(bool disposing)
        {
            if (disposing)
            {
                m_validate.Release();
                m_validate = null;
                m_correct.Release();
                m_correct = null;
            }
        }

        ~Patch()
        {
            Dispose(false);
        }

        #endregion
    }

    #endregion

    private LuaPatchManager(LuaTable listTable)
    {
        if (listTable == null || listTable.Values.Count <= 0)
            throw new ArgumentException("listTable == null || listTable.Values.Count <= 0");

        m_patches = new List<Patch>();

        foreach (string name in listTable.Values)
        {
            var patch = LuaHelper.GetLuaTable(name);
            if (patch != null)
            {
                var validateFunc = patch["Validate"] as LuaFunction;
                var correctFunc = patch["Correct"] as LuaFunction;

                if (validateFunc != null && correctFunc != null)
                    m_patches.Add(new Patch(validateFunc, correctFunc));

                patch.Release();
            }
        }

        listTable.Release();
        LuaScriptMgr.Instance.LuaGC();
    }

    public static void Load()
    {
        LuaTable listTable = null;
        string targetFileName = "LuaPatchList";

        try
        {
            listTable = LuaHelper.GetLuaTable(targetFileName);
        }
        catch
        {
            Debug.LogWarning("No file: " + targetFileName);
        }

        if (listTable != null)
        {
            if (listTable.Values.Count > 0)
                s_instance = new LuaPatchManager(listTable);

            listTable.Release();
        }
    }

    public void DoPatch(UIPanel uiPanel)
    {
        if (uiPanel == null)
            throw new ArgumentNullException("uiPanel");

        for (int i = 0; i < m_patches.Count; i++)
        {
            Patch p = m_patches[i];
            if (p.Validate(uiPanel))
            {
                p.Correct(uiPanel);
                break;
            }
        }
    }

    #region IDisposable

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    void Dispose(bool disposing)
    {
        if (disposing)
        {
            for (int i = 0; i < m_patches.Count; i++)
                m_patches[i].Dispose();

            m_patches.Clear();
            m_patches = null;
        }
    }

    ~LuaPatchManager()
    {
        Dispose(false);
    }

    #endregion
}

  lua代码如下:

LuaPatchList=
{

};

// 一个补丁例子如下:
MainInfoControllerPatch={};

local function ClickTest()
	TipsShowController.Show("Who are you?");
end

-- 验证此uiPanel是否是希望打补丁的uiPanel --
function MainInfoControllerPatch.Validate(uiPanel)
	return uiPanel.transform.parent~=nil and uiPanel.transform.parent.name=="MainInfoController(Clone)";
end

-- 纠正此uiPanel上的展示内容,执行方法等 --
function MainInfoControllerPatch.Correct(uiPanel)
	local titleLabel=uiPanel.transform:Find("LabelName"):GetComponent("UILabel");
	titleLabel.text="TianJie";

	local mustBuyButton=uiPanel.transform:Find("ButtonFashion"):GetComponent("UIButton");
	mustBuyButton.onClick:Clear();
	EventDelegate.Add(mustBuyButton.onClick,DelegateFactory.EventDelegate_Callback(ClickTest));
end

  

时间: 2024-12-15 04:39:06

unity3d热更新解决方案,使用ulua插件开发的框架。的相关文章

Unity3d热更新全书-资源加载(一)从AssetBundle说起

Unity3D动态下载资源,有没有解?有,AssetBundle就是通用解,任何一本书都会花大幅篇章来介绍AssetBundle. 我们也来说说AssetBundle 我们试全面的分析一下Unity3D提供的资源加载机制 1.Resources//内嵌资源,使用方法Resources.Load,可以加载任意种类的资源,不能动态修改,卒. 2.StreamingAssets//随包资源,使用方法IO或WWW.Load.WWW.Load可以加载任意种类资源,IO仅限bytes 和 text. 3.W

Unity热更新专题(六)uLua简介

Unity热更新专题(六)uLua简介 Unity热更新的解决方案中,uLua无疑是比较好的一种. 所以我们先来介绍一下uLua. 之前文章提到的Lua以及LuaInterface的学习,都是为了学习uLua来做基础的. 关于uLua的学习,我们可以参考官网,以及官方bbs来学习.上面有详细的视频文字教程. 官网:http://www.ulua.org/index.html 官方bbs:http://bbs.ulua.org/ 学习uLua有以上资料就够了. ==================

Unity3D热更新全书FAQ

只要有程序员朋友们问过两次的问题 就会收录在此FAQ中 1.C#Light对比LUA有什么好处 C#Light是静态类型脚本语言,语法同C#,Lua是动态类型脚本语言,这两种都有人喜欢. 我更喜欢静态类型,于是有了C#Light 2.C#Light性能怎么样 C#Light和Unilua 和ulua都做过简单性能测试,比Unilua快,和ulua各有胜负 3.C#Light IOS可以使用么 完全可以,均妥善测试 4.为什么C#Light例子和NGUI一起用会编译不过 因为Unity没有库的概念

Unity3D热更新全书-脚本(一) 初识脚本

开篇之前还是要先说明,这是一份给经验并不丰富的程序员阅读的文字. 有需求.有疑惑,往下看. 第一个问题什么是脚本?程序和脚本如何区分?我们给Unity编写的组件是程序还是脚本? 这些问题本文无意去解答,因为其中混合着太多有立场的东西,站在不同的立场会有不同的看法,这其中的矛盾不是简单可以调和的. 只要提出一个观点,就很容易陷入语言大战的泥潭. 我们不妨从另一个角度来思考,为什么要分程序和脚本,是为了找一条分界线. 这条分界线叫做灵活. 我们这个专题的出发点是探讨Unity3D客户端资源更新,已这

Unity3D热更新全书-下载 唯一的一篇

下载在这个时代实在是太平常了,每个人都深刻的理解着下载到底是什么. 这一篇文字只是把下载的代码分享并介绍,而已. 首先,下载系统担负着几个使命. 第一.是保持客户端版本库的最新. 第二.是下载要能够比对并最少下载 第三.是要尽量快一些. 其实我们并没有写一个下载系统,因为实在没有几行代码.我们这里介绍的是一个 Http下载并保存缓存,首包从StreamingAssets读取,加载资源,三个功能在一起的模块 源码位置https://github.com/lightszero/easydown 保持

Unity3D热更新全书-脚本(三) C#LightEvil语法与调试

调试,这是一个无法规避的问题 C#Light 由于有 词法解释.语法解释.运行时三种情况 所以和C#也是有类似的问题 出错大致可以分为编译错误和运行时错误 拼写出莫名的东西或者语法不正确,会在编译阶段报错,这种错误很好检查,因为 C#Light语法是C#的严格子集,所有的C#Light脚本都可以用C#的标准做语法检查 这也是C#Light基本上是用VisualStudio做编辑器的原因所在,直接作为C#代码编译,可以排除大部分的语法问题. 然后剩下的一些作为C#代码可以编译过,但是C#Light

Unity3D热更新全书-脚本(五) NGUI

让我们实际的研究一下如何将NGUI和C#LightEvil结合起来. 这里使用NGUI2.7,因为他是一个开源的版本,NGUI最新的版本未经作者的许可,是不可以带入我们的开源项目使用的. 这个例子完成的功能是从NGUI例子里找出了三个界面,按最下方的按钮依次进行切换 这是在之前的框架演示Mode1的基础上做的 由一个状态机去进行驱动,这也是我推荐各位使用脚本的方式. <=这是脚本,也是程序 Mode1的模式是定义一个接口类,然后由脚本继承此类型实现,因为随时考虑AOT的缘故(要兼容IOS),我们

Unity3d热更新全书-加载(二)如何在不用AssetBundle的前提下动态加载预设

Unity3D的主要构成大家都知道,首先是场景图,场景图上的节点构成一颗树. 每个节点对应一个GameObject对象 然后每个GameObject有若干个组件 有一些组件会与资源产生关系,比如MeshRenderer会关联材质,材质会关联shader和贴图 场景图的一部分可以被保存为一个预设,prefab. 有时候我们会需要用预设去复用,而预设的加载似乎只能通过AB去打包,其实不然,这里我们有一个开源的库就可以解决这个问题. 为什么不使用AB,可以见上一篇,加载(一),不使用AB一份资源全平台

Unity3D热更新全书-重头再来

之前写了Unity3D热更新全书系列Blog 提出了下载.加载.脚本三个方面的开源类库 下载方面有EasyDown加载方面有GameObjParser脚本方面有C#Light另外有一个没有独立成库,但是到处都用的Myjson. 2015,重头思考这些事情,有了不一样的感悟,于是也有了新的规划 1.首先了C#Light的语法解释问题变得臃肿低效,重头开始了L#项目.目前L#项目已经进入Beta阶段,完全可以全面替换C#Light.MyJson也独立成库2.下载方面,Easydown是基于经典的文件