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;

    /// <summary>
    /// 通过 Name 名,加载对应的 lua table,并将之“挂”在游戏对象上。
    /// </summary>
    /// <returns>是否加载成功</returns>
    public bool Load()
    {
        if (string.IsNullOrEmpty(Name))
            return false;

        m_table = LuaHelper.GetLuaTable(Name);
        if (m_table == null)
            return false;

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

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

        return true;
    }

    void Awake()
    {
        if (Load())
            CallMethod("Awake");
        else
        {
            if (!string.IsNullOrEmpty(Name))        // 如果 Name 为空,可能是 Add component
                throw new ArgumentNullException("Load lua table failed, no table in " + Name);
        }
    }

    void Start()
    {
        if (m_table == null)                        // 此处应为 Add component 的情况
        {
            if (string.IsNullOrEmpty(Name))
                throw new ArgumentException("string.IsNullOrEmpty(Name)");

            if (!Load())
                throw new ArgumentNullException("Load lua table failed, no table in " + Name);
        }

        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 != null ? m_table[methodName] as LuaFunction : null;
    }

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

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

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

  

其次是 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;
using UObject = UnityEngine.Object;

public static class LuaHelper
{
    #region Lua

    public static LuaTable GetLuaComponent(Transform transform)
    {
        if (transform == null)
            throw new ArgumentNullException("transform");

        var loaders = transform.GetComponents<LuaLoader>();
        var rightLoader = loaders.FirstOrDefault(lt => lt.Table != null);

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

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

    public static LuaTable GetLuaComponent(Transform transform, string type)
    {
        if (transform == null)
            throw new ArgumentNullException("transform");
        if (string.IsNullOrEmpty(type))
            throw new ArgumentException("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);
    }

    public static LuaTable AddLuaComponent(GameObject gameObject, string type)
    {
        var loader = gameObject.AddComponent<LuaLoader>();
        loader.Name = type;
        loader.Load();
        return loader.Table;
    }

    /// <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;
    }

    #endregion
}
LuaHelper

另外,关于通过 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
}
LuaPatchManager

lua代码如下:

LuaPatchList=
{
    "MainInfoControllerPatch",
};
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
Patch

转载请注明出处:http://www.cnblogs.com/jietian331/p/4955282.html

原文地址:https://www.cnblogs.com/jietian331/p/4955282.html