插件

插件允许您扩展 Solar2D 的功能。本指南概述了基本细节。

Solar2D 在 plugins.solar2d.com 运行 Solar2D 免费插件目录。如果您创建了一个插件,请将其提交到 Solar2D 免费插件目录。您可以提交一个可以获取您插件的链接,或者免费分发它。或者,您可以将其提交到 Solar2D 插件市场,请参阅 资源打包指南

架构

Solar2D 插件利用 Lua 的模块系统,其中插件通过调用 Lua require() 函数进行延迟加载。

通常,这些插件只是共享的原生库,在以下平台上受支持:macOS (.dylib), Windows (.dll),以及Android (.so).

注意
  • 在 iOS 上,不支持共享库,因此插件必须是静态库 (.a)。
  • 在 Android 上,创建纯 Java 插件 (.jar) 通常很方便。

在这两种情况下,Solar2D 添加了特殊的加载器,确保在遵循某些约定情况下可以轻松加载这些插件。

使用插件

Solar2D

Solar2D 的插件托管在多个地方。许多插件可在 免费插件目录Solar2D 市场Solar2D 插件市场 中找到。您可以通过在 build.settingsplugins 表中添加一个条目来合并插件,例如

settings =
{
    plugins =
    {
        ["plugin.myPlugin"] =
        {
            publisherId = "com.domainname"
        },
    },
}

每个插件宿主将为单个插件提供特定的 build.settings。

以这种方式添加时,Solar2D 将在构建阶段集成插件。

然后,可以使用标准 Lua require() 函数加载插件,例如

local myPlugin = require( "plugin.myPlugin" )

Solar2D Native

可以轻松地将原生插件添加到您的 iOS 或 Android 项目中。

在 iOS 上,插件将采用静态库 (.a) 文件的形式,需要将其链接到您的应用程序可执行文件中。

在 Android 上,插件可以采用 .so(共享库)、.jar.aar 文件的形式。

注意

当您通过 Solar2D Native 添加插件时,请注意以下事项

  • 资源 — 如果插件附带资源,您应确保这些资源可用。
  • 依赖项 — 如果插件依赖于链接到系统框架或库,请确保设置了适当的链接器标志。

插件项目

如果您想 创建/提交 一个插件以在 Solar2D 插件市场 中可用,您可以使用我们的 App 项目模板来简化插件的开发和测试。毕竟,插件本身不是可执行文件,它必须在实际应用程序中运行。项目模板位于 Solar2D Native 文件夹中

/Applications/Corona/Native/Project Template/App/

此文件夹有单独的特定于平台的每个目标平台的子文件夹

还有一个与这些文件夹并排放置的 Corona 文件夹,其中包含一个经典的 Corona 项目 (main.lua)。您可以修改这些文件以测试您的插件提供的 Lua API。

iOS

ios 文件夹中,有两个 Xcode 项目

  1. App.xcodeproj — 这是构建在设备上运行的 .app 可执行文件的项目。它会自动将插件构建为隐式依赖项。

  2. Plugin.xcodeproj — 对于插件开发,您应该修改此项目。它将插件构建为静态库 (.a),如果您想在 App 项目之外共享您的插件,则可以直接使用此项目。

注意

大多数情况下,您需要为您的插件创建一个通用二进制文件。换句话说,您希望发布一个同时支持设备和Xcode iOS 模拟器.

的静态库。您可以通过 lipo 工具完成此操作。在以下示例中,我们正在为 staticlibrary.a 创建一个通用库

lipo -create "/path/to/iphoneos/staticlibrary.a" "/path/to/iphoneos-simulator/staticlibrary.a" -output "/path/to/dst/staticlibrary.a"

Android

android 文件夹中,内容包括

  1. app — 此模块构建安装在设备上的 .apk。根据 app 模块的 build.gradle 脚本中 dependencies 块中的此条目 (compile project(':plugin')),app 模块自动将插件构建为显式依赖项。

  2. plugin — 此模块将插件构建为 .jar 文件,仅包含项目中 plugin/src/main/java 下的 Java 文件。如果您想在 App 项目之外共享您的插件,则可以直接使用此项目。

编写插件

插件构建在 Lua 的模块系统之上,因此它们可以有三种不同的风格

  1. 纯 Lua 代码
  2. 原生代码
  3. Lua 和原生代码的混合

纯 Lua

可以使用 CoronaLibrary 类创建 Lua 库插件。请参阅 创建 Lua 插件 指南,了解如何创建、打包并将 Lua 插件提交到可用市场的详细信息。

原生 C

除了上面讨论的插件命名约定之外,Solar2D 还希望插件遵循一些额外的约定,以确保 Lua 可以找到这些模块。在 C 中,这些约定只是模块的标准 Lua 命名约定

  • 模块函数的名称必须以 luaopen_ 为前缀。
  • 此函数的签名应与 lua_CFunction 匹配。
  • 如果模块包含点 (.),则在函数名称中将其替换为下划线 (_),因为 C 不允许符号名称中包含点。
  • 此函数应该是公开可见的。

例如,Lua 库 plugin.myLibrary 将有一个名为 luaopen_plugin_myLibrary 的 Lua 函数

static int doSomething( lua_State *L )
{
    lua_getglobal( L, "print" );
    lua_pushstring( "I did something!" );
    CoronaLuaDoCall( L, 1, 0 );
    return 0;
}

// Export so it's visible to "require()"
CORONA_EXPORT
int luaopen_plugin_myLibrary( lua_State *L )
{
    static const luaL_Reg kFunctions[] =
    {
        { "doSomething", doSomething },

        { NULL, NULL }
    };

    // Create "myLibrary"
    // Lua version assumes version and revision default to 1
    int result = CoronaLibraryNew(
        L, "myLibrary", "com.mycompany", 1, 1, kFunctions, NULL );

    return result;
}

原生 Java

如果您使用 Java 编写模块,Corona 已将 Lua 设置为加载 Java 代码,并让该 Java 代码通过 JNLua 定义 Lua 库。在这里,Lua 查找 Java 类 LuaLoader 并对其进行实例化。

Solar2D 假设以下约定

  • LuaLoader 类有一个默认(空)构造函数。
  • LuaLoader 类必须实现 JNLua 接口 com.naef.jnlua.JavaFunction
  • LuaLoader 的命名空间应与传递给 require() 的名称相同。

例如,Lua 库 plugin.myLibrary 将由(完全限定的)plugin.myLibrary.LuaLoader 实现,并且该类将实现 com.naef.jnlua.JavaFunction 接口的 invoke() 方法。

Lua + 原生

您还可以以混合方式创建插件。例如,您可以通过 Lua 代码创建库,然后在原生 C 端进行进一步的初始化。

下面是使用 Lua 创建 myLibrary 库,然后在本机 C 端添加其他函数的示例。这里,我们假设 kBuffer 存储了对应于 myLibrary.lua 的 Lua 字节码。

// The bytecodes for "myLibrary.lua" are contained in kBuffer
static const unsigned char kBuffer[] = { ... }

static int bufferLoader( lua_State *L )
{
    return luaL_loadbuffer( L, (const char*)kBuffer, sizeof( kBuffer ), "myLibrary" );
}

static int somethingElse( lua_State *L )
{
    printf( "I'm doing something else!" );
    return 0;
}

CORONA_EXPORT
int luaopen_myLibrary( lua_State *L )
{
    lua_CFunction factory = Corona::Lua::Open< bufferLoader >;
    int result = CoronaLibraryNewWithFactory( L, factory, NULL, NULL );

    if ( result )
    {
        const luaL_Reg kFunctions[] =
        {
            { "somethingElse", somethingElse },

            { NULL, NULL }
        };

        luaL_register( L, NULL, kFunctions );
    }

    return result;
}