Всем доброго времени суток!
Возникла потребность встроить рекламу в свою игру на Love2D. Решил показывать баннер после выигрыша игрока и тут появились сложности: банер вызывается Java кодом приложения, а выигрыш определяется в Lua коде. Связывает их код на Си, туториалов, как добавлять свои методы в движок не было, и пришлось копаться в коде самостоятельно. Репозиторий Love2D для Android лежит вот тут.
С чего начать?
Начать я решил с изучения метода love.system.vibrate() – метод, который появляется при использовании Love2D на андроид, а значит его где-то добавляют также, как я хочу добавить рекламу.
Если открыть класс GameActivity, то там можно найти метод vibrate, который мы ищем, и вот тут стоит объяснить, как происходит вызов Java кода из Lua.
Когда приложение только запускается, для каждого модуля Love2D создается экземпляр класса модуля и экземляр класса связки (объект, который привязывает Сишный объект к переменной Lua). У объекта связки для каждого метода, который нужно интегрировать в Lua есть свой маленький метод и список, в котором указано какой метод в Lua сопостовляется сишной связке. Выглядит это так:
// Список связок static const luaL_Reg functions[] = { { "getOS", w_getOS }, { "getProcessorCount", w_getProcessorCount }, { "setClipboardText", w_setClipboardText }, { "getClipboardText", w_getClipboardText }, { "getPowerInfo", w_getPowerInfo }, { "openURL", w_openURL }, { "vibrate", w_vibrate }, { 0, 0 } }; extern "C" int luaopen_love_system(lua_State *L) { // Эземпляр модуля System *instance = instance(); if (instance == nullptr) { instance = new love::system::sdl::System(); } else instance->retain(); // Итоговая связка WrappedModule w; w.module = instance; w.name = "system"; w.type = MODULE_ID; w.functions = functions; w.types = nullptr; return luax_register_module(L, w); }
Так же замечу, что дополнительные методы, которые добавлены для андроида, хранятся в отдельном классе, который лежит в папке ./jni/love/src/common/ и называется android.
Добавляем свой метод
Сначала создадим статичный метод в классе GameActivity:
private static GameActivity instance; @Override protected void onCreate(Bundle savedInstanceState) { instance = this; // ... } // ... // Важно, чтобы метод был статичным, потому что доступ к // экземпляру класса получить будет сложнее public static void showAd() { Toast.makeText(instance, "Ad example", Toast.LENGTH_LONG).show(); }
Теперь создадим метод в модуле love.system. Я выбрал этот модуль, потому что внедряю рекламу, но использовать именно system не обязательно. Можно даже создать свой модуль, в зависимости от того, что вам нужно.
Сначала нужно написать главную часть метода в классе android. Объявляем:
// ./jni/love/src/common/android.h bool openURL(const std::string &url); void showAd(); void vibrate(double seconds);
И создаем:
// ./jni/love/src/common/android.cpp void showAd() { // Получаем среду исполнения Java JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv(); // И класс, в котором мы создали статичный метод jclass activity = env->FindClass("org/love2d/android/GameActivity"); // Теперь сам метод jmethodID show_ad_method = env->GetStaticMethodID(activity, "showAd", "()V"); // И вызываем его env->CallStaticVoidMethod(activity, show_ad_method); env->DeleteLocalRef(activity); }
При получении идентификатора метода третим параметром мы указываем, что возвращает метод и какие параметры принимаем. Если ваш метод будет принимать аргументы, то в скобках их нужно перечислить. Если ваш метод возвращает значение, то также нужно будет изменить V на соответствующую букву. Так, чтобы описать вот такой метод:
bool isGreater(double a, double b) { return a > b; }
Будет использоваться следующая строка: (DD)Z. Подробнее можно прочитать вот тут.
Добавляем метод showAd в сам модуль:
// ./jni/love/src/modules/system/System.h /** * Shows ad */ virtual void showAd() const;
Ну и сам код:
// ./jni/love/src/modules/system/System.cpp void System::showAd() const { #ifdef LOVE_ANDROID love::android::showAd(); #endif }
Немного клея
Теперь осталось все это соединить с помощью связки. Добавляем соответсвующий метод в класс связки:
int w_showAd(lua_State *L) { instance()->showAd(); return 0; } // Ну и список связок конечно же static const luaL_Reg functions[] = { { "getOS", w_getOS }, { "getProcessorCount", w_getProcessorCount }, { "setClipboardText", w_setClipboardText }, { "getClipboardText", w_getClipboardText }, { "getPowerInfo", w_getPowerInfo }, { "openURL", w_openURL }, { "vibrate", w_vibrate }, { "showAd", w_showAd }, { 0, 0 } };
Собираем все это по туториалу с самого начала.
Добавляем код в луа (love.system.showAd()) и проверяем:

Вуаля.
Заключение
С помощью подобных манипуляций можно встраивать рекламу, управлять светодиодом, камерой и вообще делать много полезных вещей с помощью игры, что местами бывает очень полезно для интересных игровых задумок.
Спасибо за прочтение (: