您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么實(shí)現(xiàn)C#與Lua相互調(diào)用”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“怎么實(shí)現(xiàn)C#與Lua相互調(diào)用”文章能幫助大家解決問題。
使用VS2015創(chuàng)建一個(gè)空的動(dòng)態(tài)鏈接庫項(xiàng)目,刪除里面默認(rèn)創(chuàng)建的幾個(gè)文件(如果想自定義拓展可用保留),然后把Lua的源碼拷貝進(jìn)來,添加到項(xiàng)目工程中,編譯宏需要配置LUA_BUILD_AS_DLL和_CRT_SECURE_NO_WARNINGS。然后就可以編譯x86和x64的DLL動(dòng)態(tài)庫,整體步驟簡單易操作。
通過NDK編譯Android需要的so動(dòng)態(tài)庫,因此需要手寫Application.mk和Android.mk兩個(gè)mk文件,下面是我使用的兩個(gè)文件的內(nèi)容,創(chuàng)建放在上面VS的工程里面即可,路徑是在lua源碼src的上一層目錄。
# Application.mk APP_PLATFORM = android-23 APP_ABI := armeabi-v7a arm64-v8a APP_STL := stlport_shared
# Android.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) MY_FILES_PATH := $(LOCAL_PATH)/src MY_FILES_SUFFIX := %.c MY_UN_INCLUDE := %lua.c %luac.c # 遞歸遍歷目錄下的所有的文件 rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) # 獲取相應(yīng)的源文件 MY_ALL_FILES := $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*.*) ) MY_SRC_LIST := $(filter $(MY_FILES_SUFFIX),$(MY_ALL_FILES)) MY_SRC_LIST := $(filter-out $(MY_UN_INCLUDE),$(MY_SRC_LIST)) MY_SRC_LIST := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%) LOCAL_SRC_FILES = $(MY_SRC_LIST) #打印編譯信息 $(warning 'src_list='$(LOCAL_SRC_FILES)) LOCAL_MODULE := CSharpLua LOCAL_LDLIBS += -ldl LOCAL_CFLAGS := $(L_CFLGAS) include $(BUILD_SHARED_LIBRARY)
將上面的mk文件放置完成后,打開CMD命令行,執(zhí)行ndk編譯。由于并不是在Android的jni項(xiàng)目目錄,因此執(zhí)行命令會(huì)有所不同,可以使用下面的命令執(zhí)行生成,等待ndk執(zhí)行完成后就生成了需要的so庫。
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk
在Unity項(xiàng)目Assets目錄里面創(chuàng)建Plugins目錄,用于存放不同平臺(tái)的DLL庫。Windows需要的DLL存放的目錄為Assets/Plugins/x86和Assets/Plugins/x86_64;Android需要的SO文件存放的目錄為Assets/Android/[libs/arm64-v8a]括號(hào)里面的目錄其實(shí)就是上面NDK編譯后生成的路徑。
大部分的動(dòng)態(tài)庫中的接口直接使用以下這種方式即可使用,使用IntPtr來表示lua_State*對(duì)象,傳入?yún)?shù)char*可用使用byte[]或者string,但是會(huì)有一點(diǎn)點(diǎn)區(qū)別。
[DllImport("CSharpLua", EntryPoint = "luaL_newstate")] public static extern IntPtr luaL_newstate(); [DllImport("CSharpLua", EntryPoint = "luaL_openlibs")] public static extern void luaL_openlibs(IntPtr L); [DllImport("CSharpLua", EntryPoint = "luaL_loadbuffer")] public static extern int luaL_loadbuffer(IntPtr L, byte[] buff, uint size, string name); [DllImport("CSharpLua", EntryPoint = "lua_call")] public static extern void lua_call(IntPtr L, int nargs, int nresults); [DllImport("CSharpLua", EntryPoint = "lua_pcall")] public static extern int lua_pcall(IntPtr L, int nargs, int nresults, int errfunc);
1. 返回char*時(shí),不可直接使用string替換,否則調(diào)用會(huì)導(dǎo)致崩潰,因此需要像下面代碼展示的那樣進(jìn)行一下轉(zhuǎn)換才可以使用。
[DllImport("CSharpLua", EntryPoint = "lua_tolstring")] private static extern IntPtr _lua_tolstring(IntPtr L, int idx, ref uint size); public static string lua_tolstring(IntPtr L, int idx, ref uint size) { IntPtr buffer = _lua_tolstring(L, idx, ref size); return Marshal.PtrToStringAnsi(buffer); }
2. C#函數(shù)傳遞給Lua使用時(shí),需要使用delegate委托類型。
public delegate int LuaFunction(IntPtr L); [DllImport("CSharpLua", EntryPoint = "lua_pushcclosure")] public static extern void lua_pushcclosure(IntPtr L, LuaFunction func, int idx); public static void lua_pushcfunction(IntPtr L, LuaFunction func) { lua_pushcclosure(L, func, 0); }
3. 在lua源碼中定義的宏代碼是無法使用的,會(huì)提示找不到,需要在C#中手動(dòng)實(shí)現(xiàn),例如下面展示的2個(gè)宏。
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) #define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
[DllImport("CSharpLua", EntryPoint = "lua_getfield")] public static extern void lua_getfield(IntPtr L, int idx, string s); public static void lua_getglobal(IntPtr L, string s) { lua_getfield(L, LUA_GLOBALSINDEX, s); } [DllImport("CSharpLua", EntryPoint = "lua_setfield")] public static extern void lua_setfield(IntPtr L, int idx, string s); public static void lua_setglobal(IntPtr L, string s) { lua_setfield(L, LUA_GLOBALSINDEX, s); }
4. 如需要將C#的類實(shí)例對(duì)象即userdata傳遞給lua,需要在C#中轉(zhuǎn)換成IntPtr后傳遞,Lua返回的則需要通過IntPtr轉(zhuǎn)換回C#的實(shí)例對(duì)象。
[DllImport("CSharpLua", EntryPoint = "lua_pushlightuserdata")] public static extern void _lua_pushlightuserdata(IntPtr L, IntPtr p); public static void lua_pushlightuserdata<T>(IntPtr L, T p) { IntPtr obj = Marshal.GetIUnknownForObject(p); _lua_pushlightuserdata(L, obj); } [DllImport("CSharpLua", EntryPoint = "lua_touserdata")] public static extern IntPtr _lua_touserdata(IntPtr L, int idx); public static T lua_touserdata<T>(IntPtr L, int idx) { IntPtr p = _lua_touserdata(L, idx); return (T)Marshal.GetObjectForIUnknown(p); }
IntPtr L = LuaDll.luaL_newstate(); LuaDll.luaL_openlibs(L);
var data = Resources.Load<TextAsset>(lua_file); int rc = LuaDll.luaL_loadbuffer(L, data.bytes, (uint)data.bytes.Length, lua_file); rc = LuaDll.lua_pcall(L, 0, 0, 0) LuaDll.lua_getglobal(L, "main"); // 傳遞參數(shù) LuaDll.lua_pushinteger(L, 3333); LuaDll.lua_pushnumber(L, 3.3); // 執(zhí)行main方法 int i = LuaDll.lua_pcall(L, 2, 0, 0);
LuaDll.lua_pushcfunction(L, LuaPrint); LuaDll.lua_setglobal(L, "print"); [MonoPInvokeCallback] // 這個(gè)主要是在Android上需要。 static int LuaPrint(IntPtr L) { Debug.Log("....."); return 0; }
static int FindAndBind(IntPtr L) { GameObject go = LuaDll.lua_touserdata<GameObject>(L, 1); string path = LuaDll.lua_tostring(L, 2); // 這里將lua的函數(shù)放到LUA_REGISTRYINDEX上 int idx = LuaDll.luaL_refEx(L); Transform t = go.transform.Find(path); Button btn = t.GetComponent<Button>(); btn.onClick.AddListener(delegate() { // 從LUA_REGISTRYINDEX棧獲取lua的函數(shù)進(jìn)行執(zhí)行。 LuaDll.lua_rawgeti(L, LuaDll.LUA_REGISTRYINDEX, idx); LuaDll.lua_pcall(L, 0, 0, 0); }); return 0; }
關(guān)于“怎么實(shí)現(xiàn)C#與Lua相互調(diào)用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。