GVKun编程网logo

LuaJavaBridge - Lua 与 Java 互操作的简单解决方案(lua和java)

22

如果您对LuaJavaBridge-Lua与Java互操作的简单解决方案和lua和java感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解LuaJavaBridge-Lua与Java互操作的简单

如果您对LuaJavaBridge - Lua 与 Java 互操作的简单解决方案lua和java感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解LuaJavaBridge - Lua 与 Java 互操作的简单解决方案的各种细节,并对lua和java进行深入的分析,此外还有关于C++ 程序嵌 Lua(基于 LuaBridge)、CCLuaObjcBridge - Lua 与 Objective-C 互操作的简单解决方案、cocos2d-x lua LuaJavaBridge、Cocos2d-x中LuaJavaBridge使用方法的实用技巧。

本文目录一览:

LuaJavaBridge - Lua 与 Java 互操作的简单解决方案(lua和java)

LuaJavaBridge - Lua 与 Java 互操作的简单解决方案(lua和java)

LuaJavaBridge原作者文章:

LuaJavaBridge - Lua 与 Java 互操作的简单解决方案

C++ 程序嵌 Lua(基于 LuaBridge)

C++ 程序嵌 Lua(基于 LuaBridge)

配置文件搞不定的,就得依赖脚本。C++ 程序想嵌点脚本,Lua 几乎是首选。

Lua 的源码自带 Makefile,可以编译出静态库、解释器、编译器三个目标文件,作为宿主的 C++ 程序,除了要包含 Lua 头文件,还应该链接这个静态库。

如果 C++ 程序是由 CMake 来构建的,那么用 CMake 为 Lua 创建一个静态库,也不是什么难事。CMake 很好的解决了跨平台的问题。

其实脚本扩展的问题只有两个:一、怎么让 Lua 访问 C++ 对象?二、怎么让 C++ 访问 Lua 对象?当然所谓对象,是个宽泛的概念,包括变量、函数、类,等等。

通过 LuaBridge,可以很方便的解决这两个问题。

头文件

先交代一下头文件,后面就不提了。
首先包含 Lua 的几个头文件,因为是 C 代码,放在 extern "C" 里才能跟 C++ 程序混编。

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}  // extern "C"

其次是 LuaBridge 头文件,LuaBridge 跟 STL 一样,只有头文件,直接包含使用。

#include "LuaBridge/LuaBridge.h"

Lua 访问 C++

函数

C++ 函数 SayHello 「导出」为 Lua 函数 sayHello,然后通过 luaL_dostring 执行 Lua 代码,调用这个函数。

void SayHello() {
  std::cout << "Hello, World!" << std::endl;
}
int main() {
  lua_State* L = luaL_newstate();
  luaL_openlibs(L);

  luabridge::getGlobalNamespace(L)
    .addFunction("sayHello", SayHello);

  luaL_dostring(L, "sayHello()");

  lua_close(L);
}

输出:

Hello, World!

SayHello 加个参数:

void SayHello(const char* to) {
  std::cout << "Hello, " << to << "!" << std::endl;
}
luabridge::getGlobalNamespace(L)
  .addFunction("sayHello", SayHello);

luaL_dostring(L, "sayHello(''Lua'')");

输出:

Hello, Lua!

C++ 的类导出为 Lua 的表,类的成员函数对应于表的成员。假如有一个类 Line,表示文本文件中的一行:

class Line {
public:
  Line(const std::string& data)
      : data_(data) {
  }

  size_t Length() const {
    return data_.length();
  }

private:
  std::string data_;
};

构造函数

导出构造函数用 addConstructor,导出成员函数还是用 addFunction

luabridge::getGlobalNamespace(L)
  .beginClass<Line>("Line")
    .addConstructor<void(*)(const std::string&)>()
    .addFunction("getLength", &Line::Length)
  .endClass();

构造函数无法取址,调用 addConstructor 时需传递模板参数以指明类型。

测试:

const char* str =
  "line = Line(''test'')\n"
  "print(line:getLength())\n";

luaL_dostring(L, str);

输出:

4

如果有多个构造函数,比如还有一个缺省构造函数:

Line::Line();

则只能导出一个。下面这种写法,第二个会覆盖第一个:

luabridge::getGlobalNamespace(L)
  .beginClass<Line>("Line")
    .addConstructor<void(*)(void)>()  // 被下一句覆盖
    .addConstructor<void(*)(const std::string&)>()
  .endClass();

你不可能让同一个名字指代两件事情。

成员函数

考虑一个稍微复杂的成员函数,StartWith 判断一行文本是否以某个字符串打头,参数 ignore_spaces 决定是否忽略行首的空格。对实现不感兴趣的可以完全忽略。

bool Line::StartWith(const std::string& str,
                     bool ignore_spaces) const {
  size_t i = 0;

  if (ignore_spaces && !IsSpace(str[0])) {
    for (; i < data_.size() && IsSpace(data_[i]); ++i) {
    }
  }

  if (data_.size() < i + str.size()) {
    return false;
  }

  if (strncmp(&data_[i], &str[0], str.size()) == 0) {
    return true;
  }

  return false;
}

通过 addFunction 导出到 Lua:

addFunction("startWith", &Line::StartWith)

测试:

const char* str =
  "line = Line(''  if ...'')\n"
  "print(line:startWith(''if'', false))\n"
  "print(line:startWith(''if'', true))\n";

输出:

false
true

输出参数

现在为 StartWith 添加可选的输出参数,以便 ignore_spacestrue 时能够返回偏移信息(第一个非空字符的下标):

bool Line::StartWith(const std::string& str,
                     bool ignore_spaces,
                     int* off = NULL) const {
  size_t i = 0;

  if (ignore_spaces && !IsSpace(str[0])) {
    for (; i < data_.size() && IsSpace(data_[i]); ++i) {
    }
  }

  if (data_.size() < i + str.size()) {
    return false;
  }

  if (strncmp(&data_[i], &str[0], str.size()) == 0) {
    if (off != NULL) {
      *off = static_cast<int>(i);
    }
    return true;
  }

  return false;
}

输出参数在 C/C++ 里是很常见的用法,可以让一个函数返回多个值。但是用 addFunction 导出的 StartWith 并不能被 Lua 调用,因为 Lua 没有输出参数。幸运的是,Lua 的函数可以有多个返回值,为了让 StartWith 返回多个值,我们得做一层 Lua CFunction 的包装。

// Lua CFunction wrapper for StartWith.
int Line::Lua_StartWith(lua_State* L) {
  // 获取参数个数
  int n = lua_gettop(L);

  // 验证参数个数
  if (n != 3) {
   luaL_error(L, "incorrect argument number");
  }

  // 验证参数类型
  if (!lua_isstring(L, 2) || !lua_isboolean(L, 3)) {
    luaL_error(L, "incorrect argument type");
  }

  // 获取参数
  std::string str(lua_tostring(L, 2));
  bool ignore_spaces = lua_toboolean(L, 3) != 0;

  // 转调 StartWith
  int off = 0;
  bool result = StartWith(str, ignore_spaces, &off);

  // 返回结果
  luabridge::push(L, result);
  luabridge::push(L, off);
  return 2;  // 返回值有两个
}

类型为 int (*) (lua_State*) 的函数就叫 Lua CFunction。改用 addCFunction 导出 Lua_StartWith

addCFunction("startWith", &Line::Lua_StartWith)

测试:

const char* str =
  "line = Line(''  if ...'')\n"
  "ok, off = line:startWith(''if'', true)\n"
  "print(ok, off)\n";

输出:

true   2

变参

既然已经做了 CFunction 的封装,不如做得更彻底一些。鉴于 Lua 对变参的良好支持,我们让 startWith 支持变参,比如既可以判断是否以 ''if'' 打头:

line:startWith(true, ''if'')

也可以判断是否以 ''if''''else'' 打头:

line:startWith(true, ''if'', ''else'')

为此,ignore_spaces 变成了第一个参数,后面是字符串类型的变参,具体实现如下:

int Line::Lua_StartWith(lua_State* L) {
  int n = lua_gettop(L);

  if (n < 3) {
    luaL_error(L, "incorrect argument number");
  }

  if (!lua_isboolean(L, 2)) {
    luaL_error(L, "incorrect argument type");
  }

  bool ignore_spaces = lua_toboolean(L, 2) != 0;

  bool result = false;
  int off = 0;

  // 逐个比较字符串变参,一旦匹配就跳出循环。
  for (int i = 3; i <= n; ++i) {
    if (!lua_isstring(L, i)) {
      break;
    }
    std::string str(lua_tostring(L, i));
    if (StartWith(str, ignore_spaces, &off)) {
      result = true;
      break;
    }
  }

  luabridge::push(L, result);
  luabridge::push(L, off);
  return 2;
}

测试:

const char* str =
  "line = Line(''  else ...'')\n"
  "ok, off = line:startWith(true, ''if'', ''else'')\n"
  "print(ok, off)\n";

输出:

true   2

执行 Lua 文件

前面示例执行 Lua 代码全部使用 luaL_dostring,实际项目中,Lua 代码主要以文件形式存在,就需要 luaL_dofile

测试:

luaL_dofile(L, "test.lua);

文件 test.lua 的内容为:

line = Line(''  else ...'')
ok, off = line:startWith(true, ''if'', ''else'')
print(ok, off)

输出:

true   2

C++ 访问 Lua

通过 getGlobal 函数可以拿到「全局」的 Lua 对象,类型为 LuaRef

int main() {
  lua_State* L = luaL_newstate();
  luaL_openlibs(L);

  {  // 为了让 LuaRef 对象在 lua_close(L) 之前析构

    const char* str =
      "world = ''World''\n"
      "sayHello = function(to)\n"
      "    print(''Hello, '' .. to .. ''!'')\n"
      "end\n";
    luaL_dostring(L, str);

    using namespace luabridge;

    LuaRef world = getGlobal(L, "world");
    LuaRef say_hello = getGlobal(L, "sayHello");
    
    say_hello(world.cast<const char*>());
  }

  lua_close(L);
}

输出:

Hello, World!

字符串

Lua 没有字符类型,也没有 Unicode 字符串(特指 wchar_t*)。

bool IsSpace(char c) {
  return c == '' '' || c == ''\t'';
}
luabridge::getGlobalNamespace(L)
  .addFunction("isSpace", IsSpace);
    
luaL_dostring(L, "print(isSpace('' ''))");
luaL_dostring(L, "print(isSpace(''    ''))");
luaL_dostring(L, "print(isSpace(''c''))");

输出:

true
true
false

如果 IsSpace 参数为 wchar_t:

bool IsSpace(wchar_t c) {
  return c == L'' '' || c == L''\t'';
}

在 Lua 里调用 isSpace('' '') 时,LuaBridge 便会断言失败:

Assertion failed: lua_istable (L, -1), file e:\proj\lua_test\third_party\include\luabridge\detail/Us
erdata.h, line 189

折中的办法是,为 IsSpace(wchar_t c) 提供一个 wrapper,专供 Lua 使用。

bool Lua_IsSpace(char c) {
  return IsSpace((wchar_t)c);
}
luabridge::getGlobalNamespace(L)
  .addFunction("isSpace", Lua_IsSpace);

当然前提是,Lua 代码调用 isSpace 时,只会传入 ASCII 字符。

错误处理

为了方便问题诊断和错误处理,有必要为内置的函数或宏做一些封装。

luaL_dostring

bool DoLuaString(lua_State* L,
                 const std::string& str,
                 std::string* error = NULL) {
  if (luaL_dostring(L, str.c_str()) != LUA_OK) {
    if (error != NULL) {
      // 从栈顶获取错误消息。
      if (lua_gettop(L) != 0) {
        *error = lua_tostring(L, -1);
      }
    }
    return false;
  }
  return true;
}

测试:故意调用一个不存在的函数 SayHello(应该是 sayHello)。

std::string error;
if (!DoLuaString(L, "SayHello(''Lua'')", &error)) {
  std::cerr << error << std::endl;
}

输出(试图调用一个空值):

[string "SayHello(''Lua'')"]:1: attempt to call a nil value (global ''SayHello'')

luaL_dofile

luaL_dostring 的封装类似。

bool DoLuaFile(lua_State* L,
               const std::string& file,
               std::string* error = NULL) {
  if (luaL_dofile(L, file.c_str()) != LUA_OK) {
    if (error != NULL) {
      // 从栈顶获取错误消息。
      if (lua_gettop(L) != 0) {
        *error = lua_tostring(L, -1);
      }
    }
    return false;
  }

  return true;
}

luabridge::LuaRef

LuaRef world = getGlobal(L, "world");
if (!world.isNil() && world.isString()) {
  // ...
}
LuaRef say_hello = getGlobal(L, "sayHello");
if (!say_hello.isNil() && say_hello.isFunction()) {
  // ...
}

luabridge::LuaException

如果 Lua 代码有什么问题,LuaBridge 会引发 LuaException 异常,相关代码最好放在 try...catch 中。

try {
  // ...
} catch (const luabridge::LuaException& e) {
  std::cerr << e.what() << std::endl;
}

CCLuaObjcBridge - Lua 与 Objective-C 互操作的简单解决方案

CCLuaObjcBridge - Lua 与 Objective-C 互操作的简单解决方案

ccluaObjcBridge - Lua 与 Objective-C 互操作的简单解决方案

Table of Contents
    • luaoc 的主要特征
    • luaoc 用法示例
    • 从 Objective-C 调用 Lua
    • 参考

月初的时候,发了一篇关于 Lua 与 Java 互操作的文章,里面提到了我创建的 LuaJavaBridge 工具。现在,最新的 Lua 与 Objective-C 互操作工具也出来了。因为是专门针对 cocos2d-x 的,所以命名为 ccluaObjcBridge。

PS: 以前的 LuaJavaBridge 也会改名为 ccluaJavaBridge,并且参考现在 ccluaObjcBridge 的实现,做了不少改进,完成后也会发布。

ccluaObjcBridge(后文简称 luaoc)的功能就是从 Lua 里直接掉用“任意 Objective-C 类方法”。利用这个特性,封装各种 iOS 上的库简直碉堡了,堪称 cocos2d-x Lua 游戏开发的神器 ^_^

luaoc 的主要特征

  • 可以从 Lua 调用 Objective-C Class Method
  • 调用 Objective-C 方法时,支持 int/float/boolean/String/Lua function/Lua table 六种参数类型
  • 可以将 Lua function 作为参数传递给 Objective-C,并让 Objective-C 保存 Lua function 的引用
  • 可以从 Objective-C 调用 Lua 的全局函数,或者调用引用指向的 Lua function

主要功能和 luaj 是一样的,但相比老版本 luaj 做了一些针对 Objective-C 的调整。

luaoc 用法示例

下面的代码演示了如何调用 91 SDK:

Lua 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 注册回调函数,在玩家离开 91 平台界面时显示一个消息 -- PS: 91 SDK 的支付接口不会直接返回状态给客户端,支付需要在服务端验证 local function callback(event)  if event == "SDKNDCOM_LEAVE_PLATFORM" then  print(充值正在进行中,稍候您就能收到金币啦"end end  luaoc.callStaticmethodSDKNdCom", registerScriptHandler"{listener = callback})  -- 进入 91 的支付接口 local args = {  orderId = order-00001001001" coins = 1000} okret payForCoins"args) if not ok (string.formatSDKNdCom.payForCoins() - call API failure,error code: %s"tostringret))) end 

下面代码是 Objective-C 写的中间层,用来沟通 91 SDK 和 Lua 代码。

Objective-C 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+ NSDictionary *) payForCoins:)dict Nsstring *[dict objectForKey:@"orderId"];  if (!|| orderId length] == 0)  CFUUIDRef theUUID CFUUIDCreateNULL);  CFStringRef string CFUUIDCreateStringtheUUIDCFRelease[()string autorelease];  }   int ;  ([@"coins"])  [[] intValue}   description @"description"description= @"";  NdComPlatform defaultPlatform] NdUniPayForCoin:orderId  needPayCoins:coins  payDescription:];   return NSDictionary dictionaryWithObjectsAndKeys:orderId NSNumber numberWithInt:],152)!important">@"error"nil]; } 

由于无法直接获取 Objective-C 方法的参数个数和类型,所以如果要从 Objective-C 方法里接收 Lua 传递的参数,那么只能以 NSDictionary 的形式。

上述代码中,Lua 端传递了一个包含 orderId 和 coins 的表格作为参数。这个表格会被 luaoc 自动转换为 NSDictionary 对象,并传入 payForCoins:(NSDictionary*) 方法。

不过从 Objective-C 返回值给 Lua 时,就可以返回各种类型的值。目前支持的值类型有 int/float/BOOL/Nsstring/NSDictionary。特别是可以返回 NSDictionary 类型后,Lua 代码从 Objective-C 端获取数据就简单很多了。

从 Objective-C 调用 Lua

由于 ccluaObjcBridge 已经集成到quick-cocos2d-x(一个基于 cocos2d-x 的 Lua 游戏引擎)中,所以从 Objective-C 调用 Lua 也更简单灵活:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// functionId 是 Lua function 的引用 ID,参考 LuaJavaBridge 文章中的描述  // 1. 将引用 ID 对应的 Lua function 放入 Lua stack ccluaObjcBridge::pushLuaFunctionByIdfunctionId);  // 2. 将需要传递给 Lua function 的参数放入 Lua stack ccluaValueDict item; ["title"ccluaValuestringValue("hello"); "coins"("success"booleanValueTRUEgetStack()->pushccluaValueDict// 3. 执行 Lua function executeFunction1// 4. 释放引用 ID releaseLuaFunctionByIdcallbackId); 

ccluaObjcBridge::getStack() 会返回一个 ccluaStack 对象的指针。

ccluaStack 是 quick-cocos2d-x 引入的新对象,封装了 Lua stack 的一些常用操作。例如要将值放入 Lua stack 就提供了下列方法,支持各种数据类型:

1
2
3
4
5
6
7
8
9
10
void pushInt(); pushFloatfloat floatValuepushBooleanbool boolValuepushString(const char* pushNilvoidpushCCObjectCCObjectobjectValuetypeNamepushccluaValueconst & valueccluaValueDictdictpushccluaValueArrayccluaValueArrayarray); 

相比 LuaJavaBridge,ccluaObjcBridge 更容易使用、传递数据也更方便。以后 LuaJavaBridge 也会更名为 ccluaJavaBridge,并使用相同的值传递机制。

OC直接调用lua全局方法:

    cocos2d::ccluaEngine::defaultEngine()->executeGlobalFunction("luaGlobalfunctionOnPause");


参考

ccluaObjcBridge 和 LuaJavaBridge 是一脉相承,更详细的描述请参考《LuaJavaBridge - Lua 与 Java 互操作的简单解决方案》。

- 完 -

cocos2d-x lua LuaJavaBridge

cocos2d-x lua LuaJavaBridge

cocos2d-x为我们封装了LuaJavaBridge,避免了jni的繁琐,现在可以轻松的实现lua和android端的互调了,引读

LuaJavaBridge - Lua 与 Java 互操作的简单解决方案


看个例子

1、lua调用android端


lua代码

 --引入LuaJavaBridge
        local luaj = require "luaj"
        local className="com/lua/java/Test" --包名/类名
        local args = { "hello android",callbackLua }
        local sigs = "(Ljava/lang/String;I)V" --传入string参数,无返回值
        
            --luaj 调用 Java 方法时,可能会出现各种错误,因此 luaj 提供了一种机制让 Lua 调用代码可以确定 Java 方法是否成功调用。
            --luaj.callStaticmethod() 会返回两个值
            --当成功时,第一个值为 true,第二个值是 Java 方法的返回值(如果有)
            --当失败时,第一个值为 false,第二个值是错误代码
        local ok,ret = luaj.callStaticmethod(className,"test",args,sigs)
        if not ok then
           
            item:setString(ok.."error:"..ret)
            
        end

android端的代码
package com.lua.java;

/**
 * 引入Cocos2dxLuaJavaBridge
 */
import org.cocos2dx.lib.Cocos2dxLuaJavaBridge;

public class Test {
	
	public static void test(final String param,final int luaFunc){
		System.out.println("----传过来的参数----param:"+param);
		System.out.println("-------luaFunc:"+luaFunc);
		
		/**
		 * 给lua返回一个字符串
		 */
		Cocos2dxLuaJavaBridge.callLuaFunctionWithString(luaFunc,"success");
		/**
		 * 移除luaId
		 */
		Cocos2dxLuaJavaBridge.releaseLuaFunction(luaFunc);
	}
	
}


2、android端调用lua

lua代码,调用的是全局函数

function testAndroid(parameters)
    print("--testAndroid--",parameters)

cc.Director:getInstance():endToLua()

end

android端
import org.cocos2dx.lib.Cocos2dxLuaJavaBridge;
/**
* testAndroid lua中的方法名
* androidLua 传递给lua函数的参数值
*/
Cocos2dxLuaJavaBridge.callLuaGlobalFunctionWithString("testAndroid","androidLua");

Cocos2d-x中LuaJavaBridge使用方法

Cocos2d-x中LuaJavaBridge使用方法


android平台代码和Lua代码的交互均通过C++和Java交互,Lua再和C++交互

C++和Java的交互,可以参考 (*)

下面直接看用法:

lua调用java

lua中使用 luaj.callStaticMethod 来调用java静态方法:

使用场景例如提交统计,提交游戏分数


-- launch app
function JniUtil:callJavaBuyLife()
    local argsJson = nil
    local sigs = "()V"
    local luaj = require "cocos.cocos2d.luaj"
    local className = "org/cocos2dx/lua/AppActivity"
    local ok,ret = luaj.callStaticMethod(className, "pay", {argsJson}, sigs)
    if not ok then
        print("==== luaj error ==== : ", ret)
        return false
    else
        print("==== The JNI return is:", ret)
        return ret
    end
end

在AppActivity中,加入静态方法:

public static void pay() {
    // ...
}

java调用lua

cocos2d-x提供了Cocos2dxLuaJavaBridge带有如下方法可以使用:

public static native int callLuaFunctionWithString(int luaFunctionId, String value);  // 调用局部Lua-Function
public static native int callLuaGlobalFunctionWithString(String luaFunctionName, String value); // 调用全局Lua-Function
public static native int retainLuaFunction(int luaFunctionId); // retain一次Lua-Function
public static native int releaseLuaFunction(int luaFunctionId); // release掉Lua-Function

使用场景:例如我们在支付SDK中购买完了钻石后回调到游戏中

下面提供callLuaGlobalFunctionWithString用法:

function G_CallbackFromJava(msg)
    if msg == "success" then
        -- ...
    end
end

Java中对这个全局函数直接调用他,如果G_CallbackFromJava里要涉及图形计算,需要启用OpenGL线程:

sInstance.runOnGLThread(new Runnable() {
    @Override
    public void run() {
        Cocos2dxLuaJavaBridge.callLuaGlobalFunctionWithString("G_CallbackFromJava", "success");
    }
});

添加import语句:

import org.cocos2dx.lib.Cocos2dxLuaJavaBridge;

下面提供callLuaFunctionWithString用法:

function JniUtil:callJavaCallbackLua(func)
    local luaj = require "cocos.cocos2d.luaj"
    local className = "org/cocos2dx/lua/AppActivity"
    local args = { "callbacklua", func }
    local sigs = "(Ljava/lang/String;I)V"
    local ok = luaj.callStaticMethod(className,"callbackLua",args,sigs)
    if not ok then
        print("============= call callback error")
    else
        print("------------- call callback success")
    end
end

某启用充值的地方,预先注册局部函数


local function onBack(msg)
    self:resumeGame() 
end
JniUtil:getInstance():callJavaCallbackLua(onBack)

Java中可以直接回调这个函数,也可以先保存下来这个注册的Lua局部函数,一般都是保存下来,等待启用SDK充值逻辑走完后再回调

static int luaFuncCallback = 0;
public static void callbackLua(final String tipInfo,final int luaFunc){
  // 根据需要,看是否要retainLuaFunction
  luaFuncCallback = luaFunc;
  // tipInfo为Lua里传来的“callbacklua”这里没用
}

Java中充值后回调函数中调用游戏里的lua逻辑

sInstance.runOnGLThread(new Runnable() {
  @Override
  public void run() {
    Cocos2dxLuaJavaBridge.callLuaFunctionWithString(luaFuncCallback, "success");
    Cocos2dxLuaJavaBridge.releaseLuaFunction(luaFuncCallback);
  }
});

关于LuaJavaBridge - Lua 与 Java 互操作的简单解决方案lua和java的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于C++ 程序嵌 Lua(基于 LuaBridge)、CCLuaObjcBridge - Lua 与 Objective-C 互操作的简单解决方案、cocos2d-x lua LuaJavaBridge、Cocos2d-x中LuaJavaBridge使用方法的相关信息,请在本站寻找。

本文标签: