国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Unity】xLua及熱更新

這篇具有很好參考價值的文章主要介紹了【Unity】xLua及熱更新。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1 前言

????????本文主要講xLua的基本使用。看完有個基本認(rèn)識還是可以了,簡單的熱更操作還是可以做到的,但更多細(xì)節(jié)內(nèi)容依舊需要從官方文檔中了解、學(xué)習(xí)。

2 xLua

2.1 什么是xLua

????????xLua是由騰訊維護(hù)的一個開源項目,我們也可以將其看做一個插件。xLua為Unity、 .Net、 Mono等C#環(huán)境增加Lua腳本編程的能力,借助xLua,這些Lua代碼可以方便的和C#相互調(diào)用。通常作為Unity的一種熱更新解決方案。

2.2 xLua安裝

????????需要先在github上下載,地址:https://github.com/Tencent/xLua。進(jìn)入網(wǎng)址后,按如下操作下載(別問,問就是下載最新的)。

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

下載完成后,打開下載的壓縮包,將Assets文件夾中的內(nèi)容復(fù)制到Unity的Assets中即可。壓縮包Assets中內(nèi)容:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

復(fù)制到Unity Assets下的目錄(只要在Assets下就行,這里我就套了兩層文件夾):

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

PS:下載的壓縮包里也有教程文檔、案例之類的,可以跟著那個文檔學(xué)習(xí),本章基本也是基于那個文檔。xLua-master.zip\xLua-master\Assets\XLua\Doc。

2.3 簡單案例

????????在C#腳本中,我們可以通過一個lua虛擬機(jī)執(zhí)行l(wèi)ua代碼,也可以執(zhí)行C#代碼。

代碼:

using UnityEngine;
using XLua;//xLua頭文件

public class xLuaTest01 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("print('lua代碼內(nèi)容。')");
        //調(diào)用C#代碼(注意C#代碼需要前加CS.)
        luaenv.DoString("CS.UnityEngine.Debug.Log('C#代碼內(nèi)容。')");

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.4 讀取外部lua文件

2.4.1 第一種方法

????????將lua文件存儲為文本文件,然后讀取內(nèi)容執(zhí)行。首先在Resources文件夾中創(chuàng)建一個.lua文件,寫入代碼,但最后要把后綴改為.txt,代碼、文件如下圖:

print("I belong to lua script.");

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

改為.txt是為了后續(xù)讀取文本內(nèi)容。讀取并執(zhí)行代碼如下:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();
        
        //讀取lua文本文件
        TextAsset ta = Resources.Load<TextAsset>("luaContent01.lua");
        //調(diào)用Lua代碼
        luaenv.DoString(ta.text);
        
        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.4.2 第二種方法

????????使用LuaEnv自帶的require函數(shù)。比如:DoString("require 'byfile'")。

代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'luaContent01'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果一樣:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

????????require實際上是調(diào)一個個的loader去加載,有一個成功就不再往下嘗試,全失敗則報文件找不到。
????????目前xLua除了原生的loader外,還添加了從Resource加載的loader,需要注意的是因為Resource只支持有限的后綴,放Resources下的lua文件得加上txt后綴(如2.4.1所說)。
????????建議的加載Lua腳本方式是:整個程序就一個DoString("require 'main'"),然后在main.lua加載其它腳本(類似lua腳本的命令行執(zhí)行:lua main.lua)。換言之就是我們只調(diào)用一個.lua文件的內(nèi)容,后續(xù)所有內(nèi)容都已經(jīng)安排在了這個.lua文件當(dāng)中。
????????但如果Lua文件是需要下載回來的(需下載),或者某個自定義的文件格式里頭解壓出來(需解壓),或者需要解密(需解密)等等,怎么辦?還可不可以使用require?答案是:可以,直接調(diào)用require,這些工作交給Loader即可。這時候就需要了解下xLua的自定義Loader了。
????????(這些處理工作肯定是跑不掉的,問題是在哪處理。不使用require的話,我們也可以寫個萬金油方法讀取文件,獲取文本內(nèi)容,然后DoString。)

2.5 自定義Loader

????????自定義loader的本質(zhì)就是添加委托。涉及到的委托與接口如下:

public delegate byte[] CustomLoader(ref string filepath);//委托
public void LuaEnv.AddLoader(CustomLoader loader)//添加委托的方法

????????通過AddLoader可以注冊個回調(diào)函數(shù),該回調(diào)函數(shù)有一個是字符串參數(shù),lua代碼里頭調(diào)用require時,參數(shù)將會透傳給回調(diào),回調(diào)中就可以根據(jù)這個參數(shù)去加載指定文件,如果需要支持調(diào)試,需要把filepath修改為真實路徑傳出。該回調(diào)返回值是一個byte數(shù)組,如果為空表示該loader找不到,否則則為lua文件的內(nèi)容,即要執(zhí)行的內(nèi)容。

案例代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //添加自定義loader
        luaenv.AddLoader(MyLoader);

        //調(diào)用Lua代碼
        luaenv.DoString("require 'luaContent01'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }

    //自定義loader
    private byte[] MyLoader(ref string filePath)
    {
        //定義一段Lua代碼(也可以根據(jù)上面的filePath讀?。?        string content = "print('myLoader')";
        //返回此代碼
        return System.Text.Encoding.UTF8.GetBytes(content);
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

可以看到,我們最終沒有執(zhí)行l(wèi)uaContent01.lua.txt內(nèi)的lua代碼,而是執(zhí)行了我們定義的lua代碼。因為在我們注冊自定義的loader后,require時先使用了我們自定義的loader,成功獲得返回內(nèi)容后就不會再執(zhí)行其他loader了,所以最終執(zhí)行了是我們定義的lua代碼。當(dāng)然,若我們在自定義loader中返回null,獲取失敗,那么后續(xù)就會執(zhí)行自帶的loader讀取luaContent01.lua.txt內(nèi)的lua代碼,并最終執(zhí)行,這里我就不嘗試了。

2.6 C#訪問Lua的數(shù)據(jù)結(jié)構(gòu)

????????主要涉及獲取全局變量、table、全局function。三者的獲取通過訪問LuaEnv.Global就可以了,里面有個泛型Get方法,可指定返回的類型。

2.6.1 獲取全局變量

????????首先在Resources文件夾中創(chuàng)建一個CSharpCallLua.lua.txt文件,文件內(nèi)容如下:

a = 149
b = "shqz"
c = true

然后C#代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'CSharpCallLua'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //“要先執(zhí)行上面的DoString執(zhí)行Lua代碼,后續(xù)才能獲取lua代碼中的數(shù)據(jù)結(jié)構(gòu)”

        //獲取全局變量
        int aa = luaenv.Global.Get<int>("a");
        string bb = luaenv.Global.Get<string>("b");
        bool cc = luaenv.Global.Get<bool>("c");
        Debug.Log("aa:" + aa + "   bb:" + bb + "   cc:" + cc);

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.6.2 獲取Table

????????獲取table也分多方式,主要有一下四種:

  1. 映射到普通class或struct(by value:值拷貝映射)
  2. 映射到interface(by ref:引用映射)
  3. 映射到Dictionary<>,List<>(更輕量級的by value方式)
  4. 映射到LuaTable類(另外一種by ref方式)

????????首先創(chuàng)建一個.lua的文件,別忘了加.txt后綴,這里創(chuàng)建的文件全面為CSharpCallLua.lua.txt,文件內(nèi)容如下:

--全局變量
a = 149
b = "shqz"
c = true

--Table
Map = {
--table的變量
areaName = "mdsl",
areaNumber = 2,
"test1",
"test2",
123,
231,
--table的方法
ff = function(self,a,b)--這里需要加第一個參數(shù),指表本身,用.的形式也要加,用:的形式可以省略,這是lua語法的內(nèi)容。例子在下面。
print(a+b)
end
}

--其他的“table方法定義方式”
--[[
function Map:ff(a,b)--默認(rèn)自帶一個self參數(shù),表示當(dāng)前表
print(a+b)
end
--]]

--[[
function Map.ff(self,a,b)
print(a+b)
end
--]]

--既然說的第一參數(shù)問題,那也說說在lua中調(diào)用函數(shù)時也要面臨的這個問題
--在調(diào)用table中方法時:
--若是 = 創(chuàng)建,則需要以table.形式調(diào)用,且需加第一個參數(shù)
--若是 . 創(chuàng)建,則需要以table.形式調(diào)用,且需加第一個參數(shù)
--若是 : 創(chuàng)建,則需要以table:形式調(diào)用,且可不加第一個參數(shù)
--在調(diào)用C#中的成員方法時:
--以 . 形式,需加第一個參數(shù)。以 : 形式,可不加第一個參數(shù)。(這個后面講到lua調(diào)用C#也會再提)
2.6.2.1 映射到class或struct

????????定義一個class,有對應(yīng)于table的字段的public屬性,而且有無參數(shù)構(gòu)造函數(shù)即可,比如對于{f1 = 100, f2 = 100}可以定義一個包含public int f1;public int f2;的class。
????????這種方式下xLua會幫你new一個實例,并把對應(yīng)的字段賦值過去。
????????table的屬性可以多于或者少于class的屬性??梢郧短灼渌鼜?fù)雜類型。
????????要注意的是,這個過程是值拷貝,如果class比較復(fù)雜代價會比較大。而且修改class的字段值不會同步到table,反過來也不會。
????????這個功能可以通過把類型加到GCOptimize生成降低開銷,詳細(xì)可參見配置介紹文檔(與教程文檔在一起)。

代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'CSharpCallLua'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //“要先執(zhí)行上面的DoString執(zhí)行Lua代碼,后續(xù)才能獲取lua代碼中的數(shù)據(jù)結(jié)構(gòu)”

        //獲取表格,映射為class
        Map map = luaenv.Global.Get<Map>("Map");
        Debug.Log("class:   " + "name = " + map.areaName + "   number = " + map.areaNumber);

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }

    //映射用類
    class Map
    {
        public string areaName;
        public int areaNumber;
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.6.2.2 映射為interfiace

????????這種方式依賴于生成代碼(如果沒生成代碼會拋InvalidCastException異常)(接口加[CSharpCallLua]標(biāo)識,后面代碼案例里會有),代碼生成器會生成這個interface的實例,如果get一個屬性,生成代碼會get對應(yīng)的table字段,如果set屬性也會設(shè)置對應(yīng)的字段,即interface與table的數(shù)據(jù)是引用關(guān)系,修改會相互影響。另外,可以通過interface的方法訪問lua table中的函數(shù),即函數(shù)間的映射。

代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'CSharpCallLua'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //“要先執(zhí)行上面的DoString執(zhí)行Lua代碼,后續(xù)才能獲取lua代碼中的數(shù)據(jù)結(jié)構(gòu)”

        //獲取表格,inerface
        IMap imap = luaenv.Global.Get<IMap>("Map");
        Debug.Log("inerface:   " + "name = " + imap.areaName + "   number = " + imap.areaNumber);
        imap.ff(1, 2);//方法調(diào)用

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }

    //映射用接口(我這里接口還需要加public)
    [CSharpCallLua]//需加此標(biāo)簽
    public interface IMap
    {
        string areaName { get; set; }
        int areaNumber { get; set; }
        void ff(int a, int b);
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.6.2.3 映射到Dictionary<>,List<>

????????不想定義class或者interface的話,可以考慮用這個,前提t(yī)able下key類型一致,value類型一致。
????????注意,Dictionary<>,List<>是泛型的,所傳的類型參數(shù)將決定dic和list中存儲那些數(shù)據(jù),類型不匹配的內(nèi)容將不會被獲取到。

代碼:

using System.Collections.Generic;
using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'CSharpCallLua'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //“要先執(zhí)行上面的DoString執(zhí)行Lua代碼,后續(xù)才能獲取lua代碼中的數(shù)據(jù)結(jié)構(gòu)”

        //獲取表格,Dictionary<>,List<>
        Dictionary<string, int> dic = luaenv.Global.Get<Dictionary<string, int>>("Map");
        foreach (var key in dic.Keys)
        {
            Debug.Log("key: " + key + ", value: " + dic[key]);//只輸出可key為string類型,value為int類型的內(nèi)容
        }
        List<int> list = luaenv.Global.Get<List<int>>("Map");
        foreach (var data in list)
        {
            Debug.Log("list value: " + data);//只輸出了無key的int類型的內(nèi)容
        }

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.6.2.4 映射到LuaTable類

????????這種方式好處是不需要生成代碼,但也有一些問題,比如慢,比“映射為interfiace”要慢一個數(shù)量級,比如沒有類型檢查。

代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'CSharpCallLua'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //“要先執(zhí)行上面的DoString執(zhí)行Lua代碼,后續(xù)才能獲取lua代碼中的數(shù)據(jù)結(jié)構(gòu)”

        //獲取表格,LuaTable
        LuaTable tab = luaenv.Global.Get<LuaTable>("Map");
        Debug.Log("LuaTable.areaName = " + tab.Get<string>("areaName"));

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.6.3 獲取全局function

????????獲取方式分為兩種:

  1. 映射到delegate
  2. 映射到LuaFunction

????????修改CSharpCallLua.lua.txt文件的內(nèi)容,加入一個全局函數(shù),這里我直接把之前的刪除,只留一個全局函數(shù):

--全局函數(shù)
function FGlobal(p)
print("global function!")
return p,p*p
end
2.6.3.1 映射到delegate

????????這種是建議的方式,性能好很多,而且類型安全。缺點是要生成代碼(如果沒生成代碼會拋InvalidCastException異常)(委托加[CSharpCallLua]標(biāo)識)。
?????????對于function的每個參數(shù)就聲明一個輸入類型的參數(shù)。
????????多返回值要怎么處理?從左往右映射到c#的輸出參數(shù),輸出參數(shù)包括返回值,out參數(shù),ref參數(shù)(代碼中有示例)。
????????參數(shù)、返回值類型支持哪些呢?都支持,各種復(fù)雜類型,out,ref修飾的,甚至可以返回另外一個delegate。
????????delegate的使用就更簡單了,直接像個函數(shù)那樣用就可以了。
????????代碼注釋中還講了一些其他需要注意的內(nèi)容。

代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    private LuaEnv luaenv;

    //創(chuàng)建委托類型
    //注意此委托類型參數(shù),有一個out參數(shù),這是為了接受Lua函數(shù)的多值返回。
    //若Lua函數(shù)返回多個值,比如3個,函數(shù)本身返回一個,那另外兩個呢?就需要在委托中定義額外的兩個out參數(shù)來接收。
    //接收順序:函數(shù)返回、out參數(shù)順序。
    //實際除了out,也可以使用ref。
    //具體的使用例子看下面代碼。
    [CSharpCallLua]//加標(biāo)識!
    private delegate int myAction(int p,out int extraReturnParam);
    //創(chuàng)建委托對象
    myAction act;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'CSharpCallLua'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //“要先執(zhí)行上面的DoString執(zhí)行Lua代碼,后續(xù)才能獲取lua代碼中的數(shù)據(jù)結(jié)構(gòu)”

        //獲取全局函數(shù),使用委托類型
        act = luaenv.Global.Get<myAction>("FGlobal");
        //執(zhí)行函數(shù)
        int extraReturnP;
        int num = act(10, out extraReturnP);
        Debug.Log("獲取的返回值是:" + num + ",額外返回值:" + extraReturnP);

        //act是全局變量,所以這類需要置為null,釋放對函數(shù)的索引,這樣luaenv虛擬機(jī)才能在OnDestroy中正確銷毀(Dispose)
        //若act是局部變量則不用置為null,因為離開此“代碼塊”自己就會被銷毀了。
        act = null;
    }

    //這里銷毀虛擬機(jī)的代碼移動到這里是因為不能與調(diào)用全局函數(shù)的索引在一個“代碼塊”內(nèi),否則會銷毀失敗,報出異常。
    //這里實際上就是報對函數(shù)的引用沒釋放的異常,即是我們將對函數(shù)的引用變量置為null,但只要在一個“代碼塊”內(nèi),就還會報此錯誤。
    private void OnDestroy()
    {
        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.6.3.2 映射到LuaFunction

????????這種方式的優(yōu)缺點剛好和第一種相反。
????????使用也簡單,LuaFunction上有個變參的Call函數(shù),可以傳任意類型,任意個數(shù)的參數(shù),返回值是object的數(shù)組,對應(yīng)于lua的多返回值。

代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    private LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'CSharpCallLua'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //“要先執(zhí)行上面的DoString執(zhí)行Lua代碼,后續(xù)才能獲取lua代碼中的數(shù)據(jù)結(jié)構(gòu)”

        //獲取全局函數(shù),LuaFunction
        LuaFunction act = luaenv.Global.Get<LuaFunction>("FGlobal");
        //執(zhí)行函數(shù)
        object[] objs = act.Call(10);//數(shù)組接收多返回值
        foreach(object obj in objs)
        {
            Debug.Log("返回值:" + obj);
        }

        //銷毀LuaEnv對象(沒有索引煩惱,直接銷毀?。?        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.6.4 使用建議

????????訪問lua全局?jǐn)?shù)據(jù),特別是table以及function,代價比較大,建議盡量少做,比如在初始化時把要調(diào)用的lua function獲取一次(映射到delegate)后,保存下來,后續(xù)直接調(diào)用該delegate即可。table也類似。

????????如果lua側(cè)的實現(xiàn)的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一個專門的模塊負(fù)責(zé)xlua的初始化以及delegate、interface的映射,然后把這些delegate和interface設(shè)置到要用到它們的地方。(即Lua代碼內(nèi)容,完全靠委托、接口映射的思想來實現(xiàn)。然后C#這邊專門一個模塊來初始化、映射、維護(hù)這些委托、接口。我們再通過這個模塊調(diào)用我們需要的內(nèi)容。實現(xiàn)結(jié)構(gòu):使用--模塊--xLua。完成解耦。)

2.7 Lua調(diào)用C#

????????先創(chuàng)建一個LuaCallCSharp.lua.txt文件,后續(xù)將在里面寫Lua代碼。

2.7.1 new C# 對象

//在C#中new一個對象
var newGameObj = new UnityEngine.GameObject();
--在lua中new一個對象
local newGameObj = CS.UnityEngine.GameObject()

它們基本一致,但也有一些區(qū)別:

  1. lua里頭沒有new關(guān)鍵字;
  2. 所有C#相關(guān)的都放到CS下,包括構(gòu)造函數(shù),靜態(tài)成員屬性、方法;

如果有多個構(gòu)造函數(shù)呢?放心,xlua支持重載,比如你要調(diào)用GameObject的帶一個string參數(shù)的構(gòu)造函數(shù),這么寫:

--調(diào)用重載
local newGameObj2 = CS.UnityEngine.GameObject('helloworld')

????????在LuaCallCSharp.lua.txt寫入:

--創(chuàng)建一個游戲?qū)ο?,并命名?GameObject, new in lua
CS.UnityEngine.GameObject("GameObject, new in lua")

C#代碼(執(zhí)行l(wèi)ua代碼。就是前面的讀取lua文件的代碼,只是lua文件名修改了):

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    private LuaEnv luaenv;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'LuaCallCSharp'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

2.7.2 訪問C#靜態(tài)屬性,方法

--讀靜態(tài)屬性
CS.UnityEngine.Time.deltaTime
--寫靜態(tài)屬性
CS.UnityEngine.Time.timeScale = 0.5
--調(diào)用靜態(tài)方法
CS.UnityEngine.GameObject.Find('helloworld')

小技巧:如果需要經(jīng)常訪問的類,可以先用局部變量引用后訪問,除了減少敲代碼的時間,還能提高性能。如:

--用一個變量GameObject引用到UnityEngine.GameObject類
local GameObject = CS.UnityEngine.GameObject
--用變量使用類
GameObject.Find('helloworld')

案例就不演示了。

2.7.3 訪問C#成員屬性,方法

--testobj是我們假設(shè)創(chuàng)建的一個類對象

--讀成員屬性
testobj.DMF
--寫成員屬性
testobj.DMF = 1024
--調(diào)用成員方法(注意:調(diào)用成員方法,第一個參數(shù)需要傳當(dāng)前對象,這里對象指testobj,建議用冒號語法糖,如下)
testobj:DMFunc()--冒號語法糖,不需要傳當(dāng)前對象

案例就不演示了。

2.7.4 訪問C#父類屬性,方法

????????xlua支持訪問基類的靜態(tài)屬性,靜態(tài)方法(通過派生類)。支持訪問基類的成員屬性,成員方法(通過派生類實例)。

2.7.5 參數(shù)的輸入輸出屬性(out,ref)

????????Lua調(diào)用側(cè)的參數(shù)處理規(guī)則:C#的普通參數(shù)對應(yīng)一個lua輸入形參,ref修飾的參數(shù)對應(yīng)一個lua輸入形參,out不算,然后從左往右一一對應(yīng)C#與lua的參數(shù)列表。

????????Lua調(diào)用側(cè)的返回值處理規(guī)則:C#函數(shù)的返回值(如果有的話)算一個返回值,out算一個返回值,ref算一個返回值,然后從左往右對應(yīng)lua的多返回值(這個前面也有說過)。

????????來一個簡單案例說明。在LuaCallCSharp.lua.txt寫入:

--lua中的函數(shù)
function LuaFunc(l1,l2,l3)
print(l1,l2,l3)
return l1*l1,l2*l2,l3*l3
end

C#代碼:

using UnityEngine;
using XLua;

public class xLuaTest02 : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    private LuaEnv luaenv;

    //C#中定義一個委托來獲取
    [CSharpCallLua]
    private delegate int myAction(int c1, ref int c2, out int c3, int c4);
    //創(chuàng)建委托對象
    myAction act;

    private void Start()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //調(diào)用Lua代碼
        luaenv.DoString("require 'LuaCallCSharp'");//使用Loader加載(通常有多個Loader,加載時一個不成功會換下一個)

        //獲取全局函數(shù),使用委托類型
        act = luaenv.Global.Get<myAction>("LuaFunc");

        //調(diào)用方法
        int p = 2;
        int re1 = 0;
        int re2 = 0;
        re1 = act(1, ref p, out re2, 3);
        Debug.Log("返回值1:" + re1 + ",返回值2:" + p + ",返回值3:" + re2);

        //置空,為luaenv銷毀做準(zhǔn)備
        act = null;    
    }

    private void OnDestroy()
    {
        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

結(jié)果:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

首先,“Lua調(diào)用側(cè)的參數(shù)處理規(guī)則”所說的對應(yīng)關(guān)系是什么?即代碼中l(wèi)ua與C#函數(shù)的參數(shù)列表的對應(yīng)關(guān)系,在案例中體現(xiàn)為:

  • l1 → c1
  • l2 → c2
  • l2 → c4

看到?jīng)],這里沒有c3,因為c3是out修飾的,是不算參數(shù)列表對應(yīng)關(guān)系的。因此我們傳入的值應(yīng)是1、p(2)、3,而不是1、p(2)、re2(0)。圖中打印lua函數(shù)打印的參數(shù)值就是1、2、3。好,那么lua函數(shù)的返回值是什么,是每個參數(shù)的平方,即1、4、9。那么來開始討論“Lua調(diào)用側(cè)的返回值處理規(guī)則”,即返回值與參數(shù)的對應(yīng)關(guān)系,案例中體現(xiàn)為:

  • l1*l1 → act return
  • l2*l2 → p
  • l3*l3 → re2

第一個是函數(shù)返回值,后續(xù)則是ref、out修飾的參數(shù),修飾參數(shù)從左往右來一一對應(yīng)返回值,這就是它們的對應(yīng)關(guān)系。圖中我們將這幾個參數(shù)輸出,結(jié)果就是1、4、9,印證了這種關(guān)系。實際,這個返回值關(guān)系在前面也都已經(jīng)說過一次了。

2.7.6 重載方法

????????直接通過不同的參數(shù)類型進(jìn)行重載函數(shù)的訪問,例如:

--TestFunc重載1調(diào)用
testobj:TestFunc(100)
--TestFunc重載2調(diào)用
testobj:TestFunc('hello')

將分別訪問整數(shù)參數(shù)的TestFunc和字符串參數(shù)的TestFunc。

????????注意:xlua只一定程度上支持重載函數(shù)的調(diào)用,因為lua的類型遠(yuǎn)遠(yuǎn)不如C#豐富,存在一對多的情況,比如C#的int,float,double都對應(yīng)于lua的number,上面的例子中TestFunc如果有這些重載參數(shù),第一行將無法區(qū)分開來,只能調(diào)用到其中一個(生成代碼中排前面的那個)。

2.7.7 操作符

????????支持的操作符有:+,-,*,/,==,一元-,<,<=, %,[]。

2.7.8 參數(shù)帶默認(rèn)值的方法

????????和C#調(diào)用有默認(rèn)值參數(shù)的函數(shù)一樣,如果所給的實參少于形參,則會用默認(rèn)值補(bǔ)上。

2.7.9 可變參數(shù)方法

//對于C#的如下方法
void VariableParamsFunc(int a, params string[] strs)
--可以在lua里頭這樣調(diào)用
testobj:VariableParamsFunc(5, 'hello', 'john')

2.7.10 使用Extension methods

????????在C#里定義了,lua里就能直接使用。

2.7.11 泛化(模版)方法

????????不直接支持,可以通過Extension methods功能進(jìn)行封裝后調(diào)用。(就是根據(jù)需要的類型進(jìn)行封裝,一種類型組合一個封裝函數(shù),然后根據(jù)需要去調(diào)用。在封裝后使用的這一層,我們已經(jīng)失去了泛型的便利性。)(一般函數(shù)封裝也行,不一定非要Extension methods。)

????????GetComponent方法如何調(diào)用?

--沒有泛型,改為這樣:
GetComponent('組件名')

2.7.12 枚舉類型

????????枚舉值就像枚舉類型下的靜態(tài)屬性一樣。

--調(diào)用EnumTestFunc函數(shù),參數(shù)是Tutorial.TestEnum枚舉類型
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)

另外,如果枚舉類加入到生成代碼的話,枚舉類將支持__CastFrom方法,可以實現(xiàn)從一個整數(shù)或者字符串到枚舉值的轉(zhuǎn)換,例如:

--將整型轉(zhuǎn)換為枚舉類型
CS.Tutorial.TestEnum.__CastFrom(1)
--將字符串轉(zhuǎn)換為枚舉類型
CS.Tutorial.TestEnum.__CastFrom('E1')

如何將枚舉類型加入到生成代碼?給枚舉類型加上[LuaCallCSharp]標(biāo)簽即可。如:

    [LuaCallCSharp]
    enum MyEnum
    {
        one,
        two,
        three
    }

2.7.13 delegate使用(調(diào)用,+,-)

????????C#的delegate調(diào)用:和調(diào)用普通lua函數(shù)一樣

  • +操作符:對應(yīng)C#的+操作符,把兩個調(diào)用串成一個調(diào)用鏈,右操作數(shù)可以是同類型的C# delegate或者是lua函數(shù)。
  • -操作符:和+相反,把一個delegate從調(diào)用鏈中移除。

Ps:delegate屬性可以用一個luafunction來賦值。

2.7.14 event

????????比如testobj里頭有個事件定義是這樣:public event Action TestEvent; 那么lua里如何增加、移除回調(diào)?

--增加事件回調(diào)
testobj:TestEvent('+', lua_event_callback)--回調(diào)函數(shù):lua_event_callback
--移除事件回調(diào)
testobj:TestEvent('-', lua_event_callback)

2.7.15 64位整數(shù)支持

????????Lua53版本64位整數(shù)(long,ulong)映射到原生的64未整數(shù),而luajit版本,相當(dāng)于lua5.1的標(biāo)準(zhǔn),本身不支持64位,xlua做了個64位支持的擴(kuò)展庫,C#的long和ulong都將映射到userdata:

  1. 支持在lua里頭進(jìn)行64位的運(yùn)算,比較,打印
  2. 支持和lua number的運(yùn)算,比較
  3. 要注意的是,在64擴(kuò)展庫中,實際上只有int64,ulong也會先強(qiáng)轉(zhuǎn)成long再傳遞到lua,而對ulong的一些運(yùn)算,比較,我們采取和java一樣的支持方式,提供一組API,詳情請看API文檔。

2.7.16 C#復(fù)雜類型和table的自動轉(zhuǎn)換

????????對于一個有無參構(gòu)造函數(shù)的C#復(fù)雜類型,在lua側(cè)可以直接用一個table來代替,該table有對應(yīng)復(fù)雜類型中public字段的相應(yīng)字段即可。所用來代替的table支持作為函數(shù)參數(shù)傳遞,屬性賦值等,例如:

//C#下B結(jié)構(gòu)體(class也支持)定義如下
public struct A
{
    public int a;
}

public struct B
{
    public A b;
    public double c;
}

//某個類有成員函數(shù)如下
void Foo(B b)
--在lua可以這么調(diào)用
obj:Foo({b = {a = 100}, c = 200})

可以看到,為復(fù)雜類型B的形參b直接被用一個table代替了,table里還有一個table,對應(yīng)著B類型中的公共字段A類型的b。

2.7.17 獲取類型(相當(dāng)于C#的typeof)

--比如要獲取UnityEngine.ParticleSystem類的Type信息,可以這樣
typeof(CS.UnityEngine.ParticleSystem)

2.7.18 “強(qiáng)”轉(zhuǎn)

????????lua沒類型,所以不會有強(qiáng)類型語言的“強(qiáng)轉(zhuǎn)”,但有個有點像的東西:告訴xlua要用指定的生成代碼去調(diào)用一個對象,這在什么情況下能用到呢?有的時候第三方庫對外暴露的是一個interface或者抽象類,實現(xiàn)類是隱藏的,這樣我們無法對實現(xiàn)類進(jìn)行代碼生成。該實現(xiàn)類將會被xlua識別為未生成代碼而用反射來訪問,如果這個調(diào)用是很頻繁的話還是很影響性能的,這時我們就可以把這個interface或者抽象類加到生成代碼,然后指定用該生成代碼來訪問:

--也可以簡單理解為將calc轉(zhuǎn)換為后面的類型
cast(calc, typeof(CS.Tutorial.Calc))

上面就是指定用CS.Tutorial.Calc的生成代碼來訪問calc對象。

3 熱更新

3.1 配置

3.1.1 xLua配置

????????首先是xLua的配置,就是上面講的xLua安裝,拷貝幾個文件夾。

????????然后這里需要再拷貝一些新的東西,即工具包。在xLua壓縮包里有一個Tools文件,如圖:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

我們需要將其拷貝到項目當(dāng)中,放到與Assets同目錄下。

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

3.1.2 宏配置

????????配置Unity設(shè)置中的宏。如圖:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

配置好后便可在菜單欄看到新增的功能按鈕(將熱更新代碼注入到編輯器里)。如下:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

3.1.3 dll文件配置

????????需要將Unity安裝目錄下的一些.dll文件拷貝到xLua的文件目錄中。
????????.dll文件所在目錄:“ 2022.3.0f1c1\Editor\Data\Managed ”(2022.3.0f1c1是我所安裝的Unity所在文件夾,安裝時自動生成的)。文件如圖:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

將此三個文件拷貝到“ XLua\Src\Editor ”下即可。

3.2 注意的問題

3.2.1 生成代碼

????????運(yùn)行前需要!我們需要先生成代碼,再將代碼注入到編輯器內(nèi),才可以正常執(zhí)行。當(dāng)C#代碼有修改了,也不要忘了重新生成代碼。操作如圖:

【Unity】xLua及熱更新,Unity,unity,游戲引擎,lua

PS:生成代碼時我遇到了一些類型無法識別的問題,去github一看,“fix: 修復(fù)在 Unity 2022 下生成代碼報 Span 無法作泛型參數(shù)的問題 2 days ago”,兩天前剛修復(fù)的.......所以很多問題可能換個版本就解決了,要么就等大佬修復(fù)。不過官方也提供了很多問題解釋,在github上都有,可以參照官方解釋來修正錯誤。

PS:生成時,等待右下角進(jìn)度條執(zhí)行完畢再操作。

3.2.2 清除代碼

????????在生成代碼選項下的那個選項。重新生成代碼前,可以先清除(一般是不清除的,但有時候出問題報錯了可以清除試一試)。

PS:清除時,等待右下角進(jìn)度條執(zhí)行完畢再操作。

3.2.3 中文路徑

????????不要中文路徑,不管什么開發(fā),都要養(yǎng)成不用中文路徑的習(xí)慣。

3.2.4 打包項目

????????打包項目時,先把xLua中的案例全刪除,否則會報錯,無法打包。

3.2.5 開發(fā)階段關(guān)閉宏

????????建議平時開發(fā)業(yè)務(wù)代碼不打開HOTFIX_ENABLE,只在build手機(jī)版本或者要在編譯器下開發(fā)補(bǔ)丁時打開HOTFIX_ENABLE。
????????PS:我的理解是純C#開發(fā)不打開,但牽扯到Lua補(bǔ)丁開發(fā)的話就需要打開,畢竟不打開的話,使用hotfix的Lua代碼會報錯。然后若沒有Lua補(bǔ)丁,那么在build的時候也要打開,這應(yīng)該是為后續(xù)Lua補(bǔ)丁的開發(fā)做準(zhǔn)備,打開后,后續(xù)更新就可以使用Lua代碼借助hotfix來調(diào)整代碼邏輯了。

3.3 函數(shù)替換小案例

3.3.1 基本

????????官方案例就是個替換函數(shù)的案例,這里算是重新敲了一遍。首先創(chuàng)建一個腳本,將其掛載到場景當(dāng)中,代碼如下:

using UnityEngine;
using XLua;

[Hotfix]//熱更新標(biāo)簽
public class xluaTest_Hot : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Awake()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();       
    }

    [LuaCallCSharp]//加此標(biāo)簽,因為lua代碼調(diào)用了它
    private void Update()
    {
        Debug.Log("舊函數(shù)");
    }

    private void OnGUI()
    {
        //按鈕
        if (GUI.Button(new Rect(10, 10, 300, 80), "Hotfix"))
        {
            //替換xluaTest_Hot腳本的Update方法替換為自定義方法,準(zhǔn)確說是內(nèi)容替換。
            luaenv.DoString(@"
                --xlua.hotfix來執(zhí)行函數(shù)替換,參數(shù):類名、被替換函數(shù)名、替換的新函數(shù)。self是類本身(這里指xluaTest_Hot),如this。如果函數(shù)有參數(shù)的話,就繼續(xù)在self后面寫就行。
                xlua.hotfix(CS.xluaTest_Hot,'Update',function(self)
                    local a = CS.UnityEngine.GameObject.Find('Main Camera')
                    CS.UnityEngine.Debug.Log(a.name)
                end)
            ");
        }
    }

    private void OnDestroy()
    {
        //銷毀LuaEnv對象
        luaenv.Dispose();
    }
}

然后“生成代碼”、“插入代碼”,再運(yùn)行即可。點擊按鈕就可以發(fā)現(xiàn)Update函數(shù)的執(zhí)行內(nèi)容發(fā)生了變化。

????????核心思想就是,借助xlua代碼的執(zhí)行,去修改我們的代碼邏輯,比如這里的函數(shù)替換,用新邏輯替換舊邏輯。但要注意xlua代碼的執(zhí)行位置,注意邏輯間的執(zhí)行順序。xlua代碼在一個.txt文件內(nèi),所以我們可以通過更新文件的方法更新xlua文件,來更新文件內(nèi)容,來更新代碼,更新邏輯。

3.3.2 改進(jìn)

????????整理下代碼,改為讀取.txt文件中的lua代碼形式:

myLua.lua.txt

--替換xluaTest_Hot腳本的Update方法替換為自定義方法,準(zhǔn)確說是內(nèi)容替換。
--xlua.hotfix來執(zhí)行函數(shù)替換,參數(shù):類名、被替換函數(shù)名、替換的新函數(shù)。self是類本身(這里指xluaTest_Hot),如this。如果函數(shù)有參數(shù)的話,就繼續(xù)在self后面寫就行。
xlua.hotfix(CS.xluaTest_Hot,'Update',function(self)
                    local a = CS.UnityEngine.GameObject.Find('Main Camera')
                    CS.UnityEngine.Debug.Log(a.name)
                end)

myLuaDestroy.lua.txt

--將函數(shù)替換為nil,解除引用。注意,這里函數(shù)只是還原為原函數(shù)了。
xlua.hotfix(CS.xluaTest_Hot,'Update',nil)

xluaTest_Hot.cs

using UnityEngine;
using XLua;
using System.IO;

[Hotfix]//熱更新標(biāo)簽
public class xluaTest_Hot : MonoBehaviour
{
    //一個LuaEnv實例對應(yīng)Lua虛擬機(jī),出于開銷的考慮,建議全局唯一
    LuaEnv luaenv;

    private void Awake()
    {
        //創(chuàng)建LuaEnv對象
        luaenv = new LuaEnv();

        //添加自定義loader
        luaenv.AddLoader(MyLoader);

        //讀取myLua.lua.txt文件內(nèi)容,并執(zhí)行
        luaenv.DoString("require 'myLua'");
    }
    
    [LuaCallCSharp]//加此標(biāo)簽,因為lua代碼調(diào)用了它
    private void Update()
    {
        Debug.Log("舊函數(shù)");
    }

    private void OnDisable()
    {
        //讀取myLua.lua.txt文件內(nèi)容,并執(zhí)行。
        //執(zhí)行的是清除引用操作,這樣在OnDestroy里才能正常釋放虛擬機(jī),若不清除引用則會報錯。至于是否放在OnDisable里還有待討論,但肯定得在.Dispose前執(zhí)行,且不能在一個函數(shù)體內(nèi)。
        //替換函數(shù)實際是讓C#委托指向Lua中的函數(shù),委托上的函數(shù)就是我們要替換的新函數(shù),委托不為空就會去執(zhí)行委托函數(shù),我們清除引用也只是將委托清空,還原為了未替換前的狀態(tài),即后續(xù)若再執(zhí)行原函數(shù),則會執(zhí)行沒替換前的內(nèi)容。
        luaenv.DoString("require 'myLuaDestroy'");
    }

    private void OnDestroy()
    {
        //銷毀LuaEnv對象
        luaenv.Dispose();
    }

    //自定義loader
    private byte[] MyLoader(ref string filePath)
    {
        //獲取完整路徑
        string path = @"E:\_U3D\Note_Projects\009_AssetBundle_xLua\009_AssetBundle_xLua\Assets\XluaScene\Resources\" + filePath + ".lua.txt";
        //讀取文件內(nèi)容,并轉(zhuǎn)為字節(jié)數(shù)組返回(這里使用的是UTF8,要求.txt文件也是此編碼格式,否則會報錯,若是此問題則將.txt文件另存一下即可,在另存時設(shè)置UTF8格式)
        return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path));
    }
}

3.4 熱更新開發(fā)流程

  1. 首先是開發(fā)業(yè)務(wù)代碼。
  2. 在所有可能出現(xiàn)問題的類上打上[Hotfix]標(biāo)簽,即標(biāo)記我們想熱更新的類。
  3. 在所有涉及被lua調(diào)用的C#方法上打上[LuaCallCSharp]。
  4. 在所有調(diào)用lua的C#方法上打上[CSharpCallLua]。
  5. 打包發(fā)布。
  6. 后續(xù)更新時,修改代碼時(如BUG之類)只需要更新lua文件即可,修改資源時(聲音、模型、貼圖、圖片、UI等)只需要更新AB包(AssetBundle)即可。用戶只需要下載lua文件、AB包。

3.5 各種功能語法

3.5.1 替換函數(shù)

????????看3.3小案例。

3.5.2 釋放對lua函數(shù)的引用

????????看3.3小案例。

3.5.3 訪問私有成員

????????在lua腳本里,我們一般只能訪問C#類對象的公共成員,若想訪問私有程序則需要一些處理。

--加上下面這句,即可訪問xluaTest_Hot類的私有成員
xlua.private_accessible(CS.xluaTest_Hot)--私有訪問!
xlua.hotfix(CS.xluaTest_Hot,'Update',function(self)
                    --處理內(nèi)容,如self.私有成員
                end)

3.5.4 只替換函數(shù)部分

????????說是只替換部分,實際是先把原函數(shù)執(zhí)行一遍,再根據(jù)需要再在后面進(jìn)行一些修改操作。

????????首先需要一個.lua文件util.lua.txt,是官方提供的,需要將其復(fù)制到我們自己的.lua文件同目錄下(放同目錄下不是必須的,只是這樣在引用時會比較方便,只需要引用文件名即可,也可以放到別的文件夾里,但這樣在引用的時候,就需要額外加文件夾路徑。像下面代碼里,我在自己的.lua文件同目錄下創(chuàng)建了一個lib文件夾,再將其復(fù)制進(jìn)去,在引用的時候就需要加上lib)。util文件所在目錄:“XLua\Resources\xlua”。

--獲取util.lua.txt文件對象,這里lib是文件夾,與當(dāng)前.lua文件同目錄
local util = require 'lib/util'
--使用util的hotfix_ex函數(shù)來執(zhí)行替換函數(shù)操作。原之前的區(qū)別在于,這個可以調(diào)用被替換函數(shù)本身
util.hotfix_ex(CS.xluaTest_Hot,'Update',function(self)
					--調(diào)用被替換函數(shù)本身
					self:Update()
					--執(zhí)行其他操作
					local a = CS.UnityEngine.GameObject.Find('Main Camera')
					CS.UnityEngine.Debug.Log(a.name)
                end)

PS:那釋放引用的時候怎么釋放?用util.hotfix_ex來釋放嗎?不用,繼續(xù)用xlua.hotfix。

3.5.5 隨機(jī)數(shù)問題(lua中的類型轉(zhuǎn)換)

--調(diào)用C#中的隨機(jī)數(shù)函數(shù)
CS.UnityEngine.Random.Range(0,4)

--[[
存在問題:
因為lua中不區(qū)分int和float,但Range函數(shù)區(qū)分,這里會返回float類型。
若我們是使用一個C#中的int類型接收,或想要一個int類型,則需要執(zhí)行強(qiáng)轉(zhuǎn)操作,否則會給默認(rèn)0值。
--]]

--使用C#中的向下取值來處理,轉(zhuǎn)換為int類型
CS.UnityEngine.Mathf.Floor(CS.UnityEngine.Random.Range(0,4))

3.6 下載補(bǔ)丁

????????AB包就不說了,之前的AB包文章里已經(jīng)講了如何下載新的AB包。

????????然后就是Lua文件的下載。和AB包非常相似,就是在使用UnityWebRequest時有些不同,以及最后要將lua文件保存。

using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;


public class LoadFromWeb : MonoBehaviour
{
    void Start()
    {
        //開啟協(xié)程,下載file.lua.txt文件
        StartCoroutine(DownloadLuaFile(@"http://ip地址/文件目錄/file.lua.txt"));
    }

    IEnumerator DownloadLuaFile(string url)
    {
        //創(chuàng)建文件下載請求
        UnityWebRequest request = UnityWebRequest.Get(url);
        //開始請求下載并等待
        yield return request.SendWebRequest();
        //下載完成后,獲取文件內(nèi)容
        string str = request.downloadHandler.text;
        //保存文件。重寫指定路徑下file.lua.txt文件的內(nèi)容(沒有則創(chuàng)建),將內(nèi)容存儲進(jìn)去。
        File.WriteAllText(@"xxx\x\x\xx\x\file.lua.txt", str);
    }
}

3.7 標(biāo)識要熱更新的類型(Hotfix標(biāo)簽)

????????PS:這塊直接粘貼的Xlua包中文檔內(nèi)的內(nèi)容。主要說Hotfix標(biāo)簽怎么用,文檔演示的直接放在類前,這種實際上不太合適了,官方還給出了另外一種方式,如下。

和其它配置一樣,有兩種方式

方式一:直接在類里頭打Hotfix標(biāo)簽(不建議,示例只是為了方便演示采取這種方式);

!!注意,方式一在高版本unity不支持

方式二:在一個static類的static字段或者屬性里頭配置一個列表。屬性可以用于實現(xiàn)的比較復(fù)雜的配置,比如根據(jù)Namespace做白名單。

!!注意,高版本Unity需要把配置文件放Editor目錄下

//如果涉及到Assembly-CSharp.dll之外的其它dll,如下代碼需要放到Editor目錄
public static class HotfixCfg
{
    [Hotfix]
    public static List<Type> by_field = new List<Type>()
    {
        typeof(HotFixSubClass),
        typeof(GenericClass<>),
    };

    [Hotfix]
    public static List<Type> by_property
    {
        get
        {
            return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
                    where type.Namespace == "XXXX"
                    select type).ToList();
        }
    }
}

4 結(jié)束語

????????xLua的內(nèi)容到這就差不多了,基礎(chǔ)的內(nèi)容基本上都說了下,更多細(xì)節(jié)問題得去看官方的文檔,文檔就在下載的Xlua包里,前面也有說過路徑在哪,然后github上也有一些說明。想全部了解透徹的話肯定要把這些文檔教程都看看才行,當(dāng)然也可以擺爛直接用,哪里有問題了再去查。文章來源地址http://www.zghlxwxcb.cn/news/detail-770235.html

到了這里,關(guān)于【Unity】xLua及熱更新的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • unity打造基于xLua熱更新框架

    xLua是一款基于Lua的熱更新框架,它可以在游戲運(yùn)行時動態(tài)加載Lua腳本,實現(xiàn)游戲邏輯的更新。在使用xLua框架之前,我們需要先了解一些基礎(chǔ)知識。 對啦!這里有個游戲開發(fā)交流小組里面聚集了一幫熱愛學(xué)習(xí)游戲的零基礎(chǔ)小白,也有一些正在從事游戲開發(fā)的技術(shù)大佬,歡迎你

    2024年02月16日
    瀏覽(18)
  • Unity Xlua熱更新技術(shù)學(xué)習(xí)指南

    Unity Xlua熱更新技術(shù)學(xué)習(xí)指南

    熱更新是商業(yè)網(wǎng)絡(luò)游戲必不可少的一項技術(shù),而基于lua的熱更新技術(shù)又是Unity商業(yè)網(wǎng)游項目中的一個主流選擇。 部分同學(xué)近期表示自己在面試中被面試官提問熱更新相關(guān)知識,但由于對熱更新能力有所缺失,最后遺憾的與心儀工作失之交臂。 由此可見,還是有一部分同學(xué)不了

    2024年04月23日
    瀏覽(24)
  • [游戲開發(fā)][Unity] Xlua與C#互相調(diào)用規(guī)則

    [游戲開發(fā)][Unity] Xlua與C#互相調(diào)用規(guī)則

    靜態(tài)方法無需獲取類對象,獲取到類直接執(zhí)行 例1: 例2 調(diào)用非靜態(tài)方法一定要獲取到具體的C#類對象?。?! 例1:獲取單例對象并調(diào)用非靜態(tài)方法,Singleton是單例的一種寫法,網(wǎng)上源碼很多 下面是Lua調(diào)用C#的代碼,我這是模擬Xlua的工程,以類的方式實現(xiàn)交互 看Log日志發(fā)現(xiàn):

    2024年02月07日
    瀏覽(33)
  • 關(guān)于Unity在Xlua調(diào)用Lua腳本函數(shù)時報錯This type must add to CSharpCallLua 解決辦法

    使用委托來獲取xlua中的function是不行的 報錯腳本示范 即使全部接口打好標(biāo)簽并且在編輯器中把兼容等級改為4.X 打包出去還是會出問題 建議在lua腳本中建立一個空的table 再把方法塞進(jìn)去 比如 然后在c#端 就可以正常運(yùn)作這個方法了

    2024年02月12日
    瀏覽(26)
  • Unity中的熱更新的基礎(chǔ)知識,Xlua與ILRuntime基礎(chǔ)知識

    Unity中的熱更新的基礎(chǔ)知識,Xlua與ILRuntime基礎(chǔ)知識

    熱更新是指在不需要重新編譯打包游戲的情況下,在線更新游戲中的一些非核心代碼和資源,比如活動運(yùn)營和打補(bǔ)丁。熱更新分為資源熱更新和代碼熱更新兩種,代碼熱更新實際上也是把代碼當(dāng)成資源的一種熱更新,但通常所說的熱更新一般是指代碼熱更新。資源熱更新主要

    2023年04月09日
    瀏覽(25)
  • [游戲開發(fā)][Unity] Xlua生成wrap文件報錯、打AB包Wrap報錯

    ?Xlua生成wrap文件,自帶添加了ref字段報錯 例如Material生成MaterialWrap時,EnableKeyword(in LocalKeyword keyword);帶著in,所以在Wrap文件中會自動在參數(shù)前生成ref導(dǎo)致編譯不過 解決辦法: 換Xlua版本就好了,也不知道我xlua當(dāng)時從哪個版本copy過來的,換了xlua-master里的Xlua源碼

    2024年02月04日
    瀏覽(29)
  • 最全中級Unity面試題(引擎,渲染,Lua等)

    最全中級Unity面試題(引擎,渲染,Lua等)

    打算年底找工作,所以趁著年前整理波面試題。下面是部分內(nèi)容,更多內(nèi)容可以通過底部關(guān)注我的公眾號獲取。 1.UI的優(yōu)化方案 記錄最全面的ugui優(yōu)化策略_bommy游戲的博客-CSDN博客 2.圖集的壓縮格式 3.減少GC的方式 Unity優(yōu)化之GC——合理優(yōu)化Unity的GC - zblade - 博客園 Unity GC垃圾回

    2024年02月01日
    瀏覽(24)
  • 十八、Unity游戲引擎入門

    十八、Unity游戲引擎入門

    1、下載 ?? ?首先需要下載Unity Hub,下載網(wǎng)址:https://unity.com/cn。 ?? ?然后在其中下載Unity編輯器并安裝,可選擇最新版本。 ?? ?接著需要選擇適合的開發(fā)環(huán)境,例如Android Studio或Xcode,以便進(jìn)行手機(jī)游戲開發(fā)。在安裝完Unity后,需要根據(jù)項目需求下載對應(yīng)的模塊和插件,例

    2024年02月16日
    瀏覽(117)
  • Unity3D 如何把全部游戲邏輯都放到lua層實現(xiàn)詳解

    Unity3D是一款非常流行的游戲開發(fā)引擎,它支持C#、JavaScript和Boo等腳本語言。然而,有時候我們可能希望將全部游戲邏輯都放到Lua層實現(xiàn),這樣可以更方便地進(jìn)行游戲邏輯的修改和調(diào)試。本文將詳細(xì)介紹如何使用Unity3D將全部游戲邏輯都放到Lua層實現(xiàn)。 對啦!這里有個游戲開發(fā)

    2024年01月16日
    瀏覽(26)
  • 【Unity引擎技術(shù)整合】 Unity學(xué)習(xí)路線 | 知識匯總 | 持續(xù)更新 | 保持樂趣 | 共同成長

    【Unity引擎技術(shù)整合】 Unity學(xué)習(xí)路線 | 知識匯總 | 持續(xù)更新 | 保持樂趣 | 共同成長

    前言 本文對 Unity引擎 的知識進(jìn)行了一個整理總結(jié),基本包含了Unity中大部分的知識介紹。 網(wǎng)上也有很多Unity相關(guān)的學(xué)習(xí)資料,但大多數(shù)都不成體系,學(xué)起來的時候難免會東奔西走的摸不著頭腦。 本文整理的多數(shù)文章都是有對應(yīng)的 系列性文章專欄 ,可以更方便的進(jìn)行系統(tǒng)學(xué)習(xí)

    2024年02月03日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包