如果您对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)
- C++ 程序嵌 Lua(基于 LuaBridge)
- CCLuaObjcBridge - Lua 与 Objective-C 互操作的简单解决方案
- cocos2d-x lua LuaJavaBridge
- Cocos2d-x中LuaJavaBridge使用方法
LuaJavaBridge - Lua 与 Java 互操作的简单解决方案(lua和java)
LuaJavaBridge原作者文章:
LuaJavaBridge - Lua 与 Java 互操作的简单解决方案
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_spaces
为 true
时能够返回偏移信息(第一个非空字符的下标):
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 互操作的简单解决方案
-
- 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" |
下面代码是 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 |
由于无法直接获取 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* |
相比 LuaJavaBridge,ccluaObjcBridge 更容易使用、传递数据也更方便。以后 LuaJavaBridge 也会更名为 ccluaJavaBridge,并使用相同的值传递机制。
OC直接调用lua全局方法:cocos2d::ccluaEngine::defaultEngine()->executeGlobalFunction("luaGlobalfunctionOnPause");
参考
ccluaObjcBridge 和 LuaJavaBridge 是一脉相承,更详细的描述请参考《LuaJavaBridge - Lua 与 Java 互操作的简单解决方案》。
- 完 -
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使用方法
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使用方法的相关信息,请在本站寻找。
本文标签: