Corona游戏引擎允许您创建跨平台的应用程序和游戏。 但是有时他们提供的API是不够的。 在这种情况下,可以使用Corona Native ,您可以使用每个平台的本机代码来扩展功能。
本文将讨论Java在Android的Corona项目中的使用
要了解本文中发生的事情,您需要Java,Lua和Corona引擎的基本知识
开始使用
必须在计算机上安装Corona和Android Studio
Corona安装文件夹还包含项目模板:Native \ Project Template \ App。 复制整个文件夹,然后将其重命名为项目的名称。
模板定制
注意:我使用了可用于Corona- 2017.3184的最新公共版本。 在新版本中,模板可能会更改,并且不再需要本章的某些准备。
对于Android,我们需要2个文件夹: Corona和android
从Corona文件夹中删除Images.xcassets和LaunchScreen.storyboardc-我们不需要这些文件夹。 在main.lua文件中, 我们还删除了所有代码-我们将从头开始创建项目。 如果要使用现有项目,请用自己的项目替换Corona文件夹中的所有文件。
android文件夹是Android Studio的完成项目,我们需要打开它。 来自录音室的第一条消息将是“ Gradle sync failed”。 需要修复build.gradle:

要解决这种情况,请在buildscript中添加一个到存储库的链接。 我还将类路径'com.android.tools.build:gradle'中的版本更改为较新的版本。
Build.gradle代码// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.1.3' } repositories { jcenter() google() } } allprojects { repositories { jcenter() google() } } task clean(type: Delete) { delete rootProject.buildDir }
下一步是更改gradle-wrapper.properties 。 您可以通过替换distributionUrl中的gradle版本来手动更改它。 或者让工作室为您做一切。

另外,您需要修复app模块的build.gradle :在cleanAssets中,您需要添加删除行“ $ projectDir / build /中间体/ jniLibs” ,否则,您必须在每次启动前做一个干净的项目( 从此处开始)
现在同步已成功,只有很少的警告与过时的buildToolsVersion和配置中的旧语法有关。 纠正它们并不困难。
现在在工作室中,我们看到2个模块:应用程序和插件。 在继续之前,重命名应用程序(com.mycompany.app)和插件(plugin.library)。
在代码的进一步部分,该插件将称为plugin.habrExamplePlugin
该插件默认包含LuaLoader类-它将负责处理来自lua代码的调用。 已经有一些代码,但让我们清除一下。
LuaLoader代码 package plugin.habrExamplePlugin; import com.naef.jnlua.JavaFunction; import com.naef.jnlua.LuaState; @SuppressWarnings({"WeakerAccess", "unused"}) public class LuaLoader implements JavaFunction { @Override public int invoke(LuaState luaState) { return 0; } }
使用来自Lua代码的插件代码
Corona Native使用jnlua在Java和lua代码之间进行绑定。 LuaLoader实现了jnlua.JavaFunction接口,因此可以从lua代码中获取其invoke方法。 为了确保一切正常,将日志记录代码添加到LuaLoader.invoke并在main.lua中创建require插件
@Override public int invoke(LuaState luaState) { Log.d("Corona native", "Lua Loader invoke called"); return 0; }
local habrPlugin = require("plugin.habrExamplePlugin") print("test:", habrPlugin)
启动应用程序后,在日志中,我们将看到以下两行:
D / Corona本机:Lua Loader调用被称为
我/电晕:测试正确
因此,我们的应用程序下载了插件,并要求返回true。 现在,让我们尝试使用Java代码中的函数返回lua表。
要向模块添加功能,我们使用jnlua.NamedJavaFunction接口。 一个没有参数也没有返回值的简单函数的示例:
class HelloHabrFunction implements NamedJavaFunction { @Override public String getName() { return "helloHabr"; } @Override public int invoke(LuaState L) { Log.d("Corona native", "Hello Habr!"); return 0; } }
要在lua中注册我们的新函数,我们使用LuaState.register方法:
public class LuaLoader implements JavaFunction { @Override public int invoke(LuaState luaState) { Log.d("Corona native", "Lua Loader invoke called"); String libName = luaState.toString(1);
此代码需要其他说明:
LuaState是invoke方法的参数,本质上表示Lua虚拟机上的包装器(如果我输入错了,请纠正我)。 对于那些熟悉使用C语言编写的lua代码的人来说,LuaState与C语言中的lua_State指针相同。
对于那些想深入研究lua的人,我建议您阅读《应用程序接口》一书中的手册。
因此,当调用invoke时,我们得到LuaState。 它具有一个堆栈,其中包含从lua代码传递给我们的函数的参数。 在这种情况下,这是模块的名称,因为在调用require(“ plugin.habrExamplePlugin”)时执行LuaLoader。
invoke返回的数字显示了堆栈中将返回到lua代码的变量数。 在require的情况下,该数字无效,但是稍后我们将通过创建返回几个值的函数来使用此知识。
向模块添加字段
除了功能,我们还可以向模块添加其他字段,例如版本:
luaState.register(libName, luaFunctions);
在这种情况下,我们使用-2索引指示需要为我们的模块设置字段。 负索引表示计数从堆栈末尾开始。 -1将指向字符串“ 0.1.2”(在lua中,索引以1开头)。
为了不阻塞堆栈,设置字段后,建议调用luaState.pop(1)-从堆栈中抛出1个元素。
完整的LuaLoader代码 @SuppressWarnings({"WeakerAccess", "unused"}) public class LuaLoader implements JavaFunction { @Override public int invoke(LuaState luaState) { Log.d("Corona native", "Lua Loader invoke called"); String libName = luaState.toString(1); // ( require) NamedJavaFunction[] luaFunctions = new NamedJavaFunction[]{ new HelloHabrFunction(), // }; luaState.register(libName, luaFunctions); // , luaState.register(libName, luaFunctions); // , luaState.pushString("0.1.2"); // luaState.setField(-2, "version"); // version . // 1 lua . // require , require return 0; } }
功能实例
一个函数示例,该函数采用多个字符串并通过“字符串”构建器将它们连接起来实现方式:
class StringJoinFunction implements NamedJavaFunction{ @Override public String getName() { return "stringJoin"; } @Override public int invoke(LuaState luaState) { int currentStackIndex = 1; StringBuilder stringBuilder = new StringBuilder(); while (!luaState.isNone(currentStackIndex)){ String str = luaState.toString(currentStackIndex); if (str != null){
在lua中的用法:
local joinedString = habrPlugin.stringJoin("this", " ", "was", " ", "concated", " ", "by", " ", "Java", "!", " ", "some", " ", "number", " : ", 42); print(joinedString)
返回多个值的示例SumFunction类实现NamedJavaFunction {
覆写
公共字符串getName(){
返回“和”;
}
@Override public int invoke(LuaState luaState) { if (!luaState.isNumber(1) || !luaState.isNumber(2)){ luaState.pushNil(); luaState.pushString("Arguments should be numbers!"); return 2; } int firstNumber = luaState.toInteger(1); int secondNumber = luaState.toInteger(1); luaState.pushInteger(firstNumber + secondNumber); return 1; }
}
Java Reflection-在lua中直接使用Java类
jnlua库具有一个特殊的JavaReflector类,该类负责根据Java对象创建lua表。 因此,您可以使用Java编写类,并将其提供给lua代码以备将来使用。
要做到这一点很简单:
类的例子
@SuppressWarnings({"unused"}) public class Calculator { public int sum(int number1, int number2){ return number1 + number2; } public static int someStaticMethod(){ return 4; } }
将此类的实例添加到我们的模块中
luaState.pushJavaObject(new Calculator()); luaState.setField(-2, "calc"); luaState.pop(1);
在Lua中的用法:
local calc = habrPlugin.calc print("call method of java object", calc:sum(3,4)) print("call static method of java object", calc:getClass():someStaticMethod())
注意类方法调用中的冒号。 对于静态方法,还必须使用冒号。
然后,我注意到了反射器的一个有趣的功能:如果我们仅将类的实例传递给lua,则可以通过getClass()调用其静态方法。 但是在通过getClass()调用之后,后续调用将在对象本身上触发:
print("call method of java object", calc:sum(3,4))
另外,使用getClass(),我们可以直接在lua中创建新对象:
local newInstance = calc:getClass():new()
不幸的是,由于setField内部存在“ java.lang.IllegalArgumentException:非法类型” ,因此我无法在模块字段中保存Calculator.class。
动态创建和调用lua函数
之所以出现此部分,是因为Crown无法提供直接使用Java从其api访问功能的功能。 但是jnlua.LuaState允许您加载和执行任意lua代码:
class CreateDisplayTextFunction implements NamedJavaFunction{
切记通过LuaLoader.invoke注册该函数,类似于前面的示例
lua中的呼叫:
habrPlugin.createText("Hello Habr!")
结论
因此,您的android应用程序可以使用平台的所有本机功能。 该解决方案的唯一缺点是您失去了使用Corona Simulator的能力,这会减慢开发速度(重新启动模拟器几乎是瞬间的,这与在需要构建+安装的模拟器或设备上进行调试不同)
有用的链接
完整代码可在github上找到
Corona本机文档
3) jnlua存储库之一 。 帮助我了解某些功能的目的。