如何在Java中使用Lua脚本语言是本文要介绍的内容,主要是来学习LUA脚本语言在JAVA中如何来使用,Lua就不说了, 现在比较热门, 语法也很简单. 为了在Java中调用, 折腾了比较长的时间, 就把一些东西记在下面.来看详细内容讲解。
Lua是支持内嵌在C程序中的, 但是官方不支持Java. 在网上查了下, 有LuaJava开源库, 拿来试用了一下, 发现这个库还算比较完善的. 地址是
http://www.keplerproject.org/luajava/
- 1.
这个LuaJava实际上就是按照Lua官方文档, 把Lua的C接口通过JNI包装成Java的库. 下载, 里面是一个.dll, 一个.jar. 把.dll放到java.library.path下, 再把.lib放到classpath中, helloworld运行OK.
但是, 测试的时候, 很快发现了第一个问题: 在调用LuaJava中提供的LuaState.pushInteger 方法的时候, 出现了错误 : Unsatisfied Link Error. 其他的LuaState.pushNumber方法倒是没有问题. 用Depends工具看了下, 这个.dll居然没有导出pushInteger这个函数. 晕....
下载LuaJava的源代码, 查看了下Luajava.c 和 Luajava.h, 发现果然里面有点问题, 在.h里面定义了JNI中对应Java函数的C函数
JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushInteger
但是.c中没有实现这个函数. 无语, 看来大马虎哪都有啊. 幸亏有源代码, 照猫画虎在Luajava.c中加上这个函数的实现,
JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushInteger
(JNIEnv * env, jobject jobj, jobject cptr, jint i)
{
lua_State * L = getStateFromCPtr( env , cptr );
lua_pushinteger(L, i);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
然后编译. 编译也出现了问题了, 官方文档中说可以用VC++来Build, 但是没有说官方用的是什么版本. 我用VC2005就不行. 好在Luajava比较小, 就一个.h 一个 .c , 在VC中新建一个.dll项目, 把文件加进去, 修改一下build参数 (Include 需要加上lua的头文件, lib中需要加上lua的.lib文件, 另外要选上 Compile as C Code (/TC) ) Build, 通过了.
这时再在Java中调用pushInteger方法就没有问题了.
在测试中, 发现Luajava提供的文档中, 对于Lua脚本怎么调用Java对象/方法很详细, 但是在Java中怎么调用Lua函数/取得返回值 就没有. 参考了http://www.lua.org/manual/5.1/manual.html#lua_CFunction 的Lua C文档, 实现了传递对象到Lua中并取得返回值的代码:
Test1: 测试传递简单类型, 并取得返回值:
Lua 脚本(test.lua):
function test(a,b)
return a+b
end
- 1.
- 2.
- 3.
Java代码:
static {
//加载Lua5.1.dll, 因为LuaJava最后还是要调用Lua的东西
System.loadLibrary("lua5.1");
}
public static void main(String[] argu) throws LuaException {
LuaState L = LuaStateFactory.newLuaState();
L.openLibs();
//读入Lua脚本
int error = L.LdoFile("test.lua");
if (error != 0) {
System.out.println("Read/Parse lua file error. Exit.");
return;
}
//找到函数test
L.getField(LuaState.LUA_GLOBALSINDEX, "test");
//参数1压栈
L.pushInteger(1);
//参数2压栈
L.pushInteger(2);
//调用!! 一共两个参数, 1个返回值
L.call(2, 1);
//保存返回值, 到a中
L.setField(LuaState.LUA_GLOBALSINDEX, "a");
//读入a
LuaObject l = L.getLuaObject("a");
//打印结果.
System.out.println("Result is " + l.getString());
L.close();
}
- 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.
- 31.
- 32.
- 33.
- 34.
测试2: 传递Java对象
class Value {
public int i;
public void inc() {
i++;
}
public int get() {
return i;
}
public String toString() {
return "Value is " + i;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
Lua脚本: (该脚本中调用两次对象的inc方法, 并调用get方法输出结果)
function test1(v)
v:inc();
v:inc();
print("In lua: " .. v:get());
return v
end
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
Java 代码: (前面都一样, 略)
//找到函数test1
L.getField(LuaState.LUA_GLOBALSINDEX, "test1");
//生成新的对象供测试
Value v = new Value();
//对象压栈
L.pushObjectValue(v);
//调用函数test1, 此时1个参数, 1个返回值
L.call(1, 1);
//结果放在b中.
L.setField(LuaState.LUA_GLOBALSINDEX, "b");
LuaObject l = L.getLuaObject("b");
System.out.println("Result is " + l.getObject());
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
运行结果:
Result is Value is 2
In lua: 2
- 1.
- 2.
和预期的一致.
实现一个怪物的创建,把lua里的设定当作初始状态传给monstor,名字为sample monstor,防御10,攻击10,生命100
1.先导入lib--luajava-1.1.jar
import org.keplerproject.luajava.LuaState;
import org.keplerproject.luajava.LuaStateFactory;
public class Load{
LuaState luaState;
/**
* Constructor
* @param fileName File name with Lua .
*/
Load(final String fileName) {
this.luaState = LuaStateFactory.newLuaState();
this.luaState.openLibs();
this.luaState.LdoFile(fileName);
}
/**
* Ends the use of Lua environment.
*/
void close() {
this.luaState.close();
}
/**
* Call a Lua inside the Lua to insert
* data into a Java object passed as parameter
* @param Name Name of Lua .
* @param obj A Java object.
*/
void run(String Name, Object obj) {
this.luaState.getGlobal(Name);
this.luaState.pushJavaObject(obj);
this.luaState.call(1,0);
}
}
public class Monster{
/* Info */
protected String race;
protected int defense;
protected int attack;
protected int life;
/* */
private Load ;
public Monster(String race) {
/* Loads Lua for this race.*/
this. = new Load(race+".lua");
/*Call Lua create .*/
.run("create", this);
}
public void setRace(String race) {
this.race = race;
}
public String getRace() {
return race;
}
public int getDefense() {
return this.defense;
}
public void setDefense(int defense) {
this.defense = defense;
}
public int getLife() {
return this.life;
}
public void setLife(int life) {
this.life = life;
}
public void setAttack(int attack) {
this.attack = attack;
}
public int getAttack() {
return this.attack;
}
}
monstor.lua---
create(monster)
monster:setRace("Sample Monster")
monster:setDefense(10)
monster:setAttack(10)
monster:setLife(100)
end
- 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.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
但总是抛出这个错误:
PANIC: unprotected error in call to Lua API (Invalid method call. No such method.)
- 1.
不知为何,以后用到的时候再research.
已经查出来,原来在Monster类中少了个方法:
public void setRace(String race) {
this.race = race;
}
- 1.
- 2.
- 3.
怪不得会找不到,
要在一lua文件a.lua里导入其他的lua文件b.lua,用require "b"
如果要从lua中运算后得到返回参数,则需要做一下修改:在lua文件中改成:
create(monster)
monster:setRace("Sample Monster")
monster:setDefense(10)
monster:setAttack(10)
monster:setLife(100)
return monster
end
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
在Load.java中的run改成如下:
void run(String Name, Object obj) {
this.luaState.getGlobal(Name);
this.luaState.pushJavaObject(obj);
this.luaState.call(1, 1);// 一个参数,0个返回
try {
Object object =luaState.getObjectFromUserdata(1);
} catch (LuaException e) {
e.printStackTrace();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
小结:如何在Java中使用Lua脚本语言的内容介绍完了,希望通过本文的学习能对你有所帮助!