Corona Native untuk Android - menggunakan kode Java khusus dalam permainan yang ditulis dalam Corona

Mesin game Corona memungkinkan Anda membuat aplikasi dan game lintas platform. Namun terkadang API yang mereka berikan tidak cukup. Untuk kasus seperti itu, ada Corona Native , yang memungkinkan Anda untuk memperluas fungsionalitas menggunakan kode asli untuk setiap platform.


Artikel ini akan membahas penggunaan Java dalam proyek Corona untuk android


Untuk memahami apa yang terjadi di artikel, Anda memerlukan pengetahuan dasar tentang Java, Lua dan mesin Corona


Memulai


Corona dan Android Studio harus diinstal di komputer


Folder instalasi Corona juga berisi templat proyek: Native \ Project Template \ App. Salin seluruh folder dan ganti namanya menjadi nama proyek Anda.


Kustomisasi template


Catatan: Saya menggunakan bangunan publik terbaru yang tersedia untuk Corona - 2017.3184 . Dalam versi baru, templat dapat berubah, dan beberapa persiapan dari bab ini tidak lagi diperlukan.


Untuk android kita membutuhkan 2 folder di dalamnya: Corona dan android


Dari folder Corona , hapus Images.xcassets dan LaunchScreen.storyboardc - kita tidak memerlukan folder ini. Di file main.lua , kami juga menghapus semua kode - kami akan mulai membuat proyek dari awal. Jika Anda ingin menggunakan proyek yang sudah ada, maka ganti semua file di folder Corona dengan milik Anda


Folder android adalah proyek yang selesai untuk Android Studio, kita harus membukanya. Pesan pertama dari studio adalah "Sinkronisasi tingkat gagal". Perlu untuk memperbaiki build.gradle:


membangun gradle


Untuk memperbaiki situasi, tambahkan tautan ke repositori di buildscript. Saya juga mengubah versi di classpath 'com.android.tools.build:gradle' menjadi yang lebih baru.


Kode 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 } 

Langkah selanjutnya adalah mengubah gradle-wrapper.properties . Anda dapat mengubahnya secara manual dengan mengganti versi gradle di distributionUrl . Atau biarkan studio melakukan segalanya untuk Anda.


gradle-wrapper.properties


Selain itu, Anda perlu memperbaiki build.gradle untuk modul aplikasi: di cleanAssets Anda perlu menambahkan baris hapus "$ projectDir / build / intermediates / jniLibs" , yang tanpanya Anda harus melakukan proyek bersih sebelum setiap permulaan ( diambil dari sini )


Sekarang sinkronisasi berhasil, hanya ada beberapa peringatan yang terkait dengan buildToolsVersion usang dan sintaks lama dalam konfigurasi. Memperbaiki mereka tidak sulit.


Sekarang di studio kita melihat 2 modul: aplikasi dan plugin. Ganti nama aplikasi (com.mycompany.app) dan plugin (plugin.library) sebelum melanjutkan.


Lebih lanjut dalam kode, plugin akan disebut plugin.habrExamplePlugin


Plugin ini berisi kelas LuaLoader secara default - itu akan bertanggung jawab untuk menangani panggilan dari kode lua. Sudah ada beberapa kode, tapi mari kita bersihkan.


Kode 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; } } 

Menggunakan kode plugin dari kode lua


Corona Native menggunakan jnlua untuk mengikat kode java dan lua. LuaLoader mengimplementasikan antarmuka jnlua.JavaFunction, sehingga metode yang dipanggil tersedia dari kode lua. Untuk memastikan semuanya beres, tambahkan kode logging ke LuaLoader.invoke dan buat plugin yang membutuhkan di main.lua


  @Override public int invoke(LuaState luaState) { Log.d("Corona native", "Lua Loader invoke called"); return 0; } 

 local habrPlugin = require("plugin.habrExamplePlugin") print("test:", habrPlugin) 

Setelah meluncurkan aplikasi, di antara log kita akan melihat 2 baris berikut:


D / Corona asli: Lua Loader memanggil dipanggil
I / Corona: tes benar

Jadi, aplikasi kami mengunduh plugin, dan meminta pengembalian yang benar. Sekarang mari kita coba mengembalikan tabel lua dengan fungsi dari kode Java.


Untuk menambahkan fungsi ke modul, kita menggunakan antarmuka jnlua.NamedJavaFunction. Contoh fungsi sederhana tanpa argumen dan tanpa nilai balik:


 class HelloHabrFunction implements NamedJavaFunction { @Override public String getName() { return "helloHabr"; } @Override public int invoke(LuaState L) { Log.d("Corona native", "Hello Habr!"); return 0; } } 

Untuk mendaftarkan fungsi baru kami di lua, kami menggunakan metode 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); //      (  require) NamedJavaFunction[] luaFunctions = new NamedJavaFunction[]{ new HelloHabrFunction(), //     }; luaState.register(libName, luaFunctions); //   ,     //  1        lua . //     require      , require     return 1; } 

Kode ini membutuhkan penjelasan tambahan:


LuaState, parameter dari metode invoke, pada dasarnya mewakili pembungkus atas mesin virtual Lua (tolong perbaiki saya jika saya salah menuliskannya). Bagi mereka yang terbiasa menggunakan kode lua dari C, LuaState sama dengan pointer lua_State di C.


Bagi mereka yang ingin mempelajari hutan bekerja dengan lua, saya sarankan membaca manual, dimulai dengan Program Aplikasi Antarmuka


Jadi, ketika dipanggil, kita mendapatkan LuaState. Ini memiliki tumpukan yang berisi parameter yang diteruskan ke fungsi kami dari kode lua. Dalam hal ini, ini adalah nama modul, karena LuaLoader dijalankan ketika panggilan membutuhkan ("plugin.habrExamplePlugin").


Angka yang dikembalikan oleh invoke menunjukkan jumlah variabel dari tumpukan yang akan dikembalikan ke kode lua. Jika diperlukan, angka ini tidak berpengaruh, tetapi kami akan menggunakan pengetahuan ini nanti dengan membuat fungsi yang mengembalikan beberapa nilai


Menambahkan bidang ke modul


Selain fungsi, kami juga dapat menambahkan bidang tambahan ke modul, misalnya versi:


  luaState.register(libName, luaFunctions); //   ,       luaState.pushString("0.1.2"); //     luaState.setField(-2, "version"); //   version   . 

Dalam hal ini, kami menggunakan indeks -2 untuk menunjukkan bahwa bidang perlu diatur untuk modul kami. Indeks negatif berarti bahwa penghitungan dimulai pada akhir tumpukan. -1 akan menunjuk ke string "0.1.2" (dalam lua, indeks dimulai dengan satu).


Agar tidak menyumbat tumpukan, setelah mengatur bidang, saya sarankan memanggil luaState.pop (1) - melempar 1 elemen dari tumpukan.


Kode LuaLoader lengkap
 @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; } } 

Contoh Fungsi


Contoh fungsi yang mengambil banyak string dan menggabungkannya melalui pembangun String

Implementasi:


 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){ //toString  null  non-string  non-number,  stringBuilder.append(str); } currentStackIndex++; } luaState.pushString(stringBuilder.toString()); return 1; } } 

Penggunaan dalam lua:


 local joinedString = habrPlugin.stringJoin("this", " ", "was", " ", "concated", " ", "by", " ", "Java", "!", " ", "some", " ", "number", " : ", 42); print(joinedString) 

Contoh mengembalikan beberapa nilai

kelas SumFunction mengimplementasikan NamedJavaFunction {
Timpa
public String getName () {
kembalikan "jumlah";
}


 @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 - menggunakan kelas Java langsung di lua


Perpustakaan jnlua memiliki kelas JavaReflector khusus yang bertanggung jawab untuk membuat tabel lua dari objek java. Dengan demikian, Anda dapat menulis kelas dalam java dan memberikannya ke kode lua untuk digunakan di masa depan.


Untuk melakukan ini cukup sederhana:


Contoh kelas


 @SuppressWarnings({"unused"}) public class Calculator { public int sum(int number1, int number2){ return number1 + number2; } public static int someStaticMethod(){ return 4; } } 

Menambahkan instance kelas ini ke modul kami


  luaState.pushJavaObject(new Calculator()); luaState.setField(-2, "calc"); luaState.pop(1); 

Penggunaan di 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()) 

Perhatikan titik dua dalam panggilan metode kelas. Untuk metode statis, Anda juga harus menggunakan titik dua.


Kemudian saya perhatikan fitur reflektor yang menarik: jika kita hanya mengirimkan instance kelas ke lua, maka memanggil metode statisnya dimungkinkan melalui getClass (). Tetapi setelah panggilan melalui getClass (), panggilan selanjutnya akan dipicu pada objek itu sendiri:


 print("call method of java object", calc:sum(3,4)) -- ok print("exception here", calc:someStaticMethod()) --   "com.naef.jnlua.LuaRuntimeException: no method of class plugin.habrExamplePlugin.Calculator matches 'someStaticMethod()'" print("call static method of java object", calc:getClass():someStaticMethod()) -- ok print("hmm", calc:someStaticMethod()) --    getClass         

Selain itu, menggunakan getClass (), kita dapat membuat objek baru secara langsung di lua:


 local newInstance = calc:getClass():new() 

Sayangnya, saya tidak dapat menyimpan Calculator.class di bidang modul karena "java.lang.IllegalArgumentException: tipe ilegal" di dalam setField .


Membuat dan memanggil fungsi lua dengan cepat


Bagian ini muncul karena mahkota tidak menyediakan kemampuan untuk mengakses fungsi dari apinya secara langsung di Jawa. Tapi jnlua.LuaState memungkinkan Anda memuat dan mengeksekusi kode lua yang berubah-ubah:


 class CreateDisplayTextFunction implements NamedJavaFunction{ //    API  private static String code = "local text = ...;" + "return display.newText({" + "text = text," + "x = 160," + "y = 200," + "});"; @Override public String getName() { return "createText"; } @Override public int invoke(LuaState luaState) { luaState.load(code,"CreateDisplayTextFunction code"); //    ,     luaState.pushValue(1); //        luaState.call(1, 1); //   ,      1 ,    1 return 1; } } 

Ingatlah untuk mendaftarkan fungsi melalui LuaLoader.invoke, mirip dengan contoh sebelumnya


Panggilan dalam lua:


 habrPlugin.createText("Hello Habr!") 

Kesimpulan


Dengan demikian, aplikasi Android Anda dapat menggunakan semua kemampuan asli platform. Satu-satunya kelemahan dari solusi ini adalah Anda kehilangan kemampuan untuk menggunakan Corona Simulator, yang memperlambat pengembangan (memulai ulang simulator hampir seketika, tidak seperti debugging pada emulator atau perangkat yang membutuhkan build + install)


Tautan yang bermanfaat


  1. Kode lengkap tersedia di github


  2. Dokumentasi Asli Korona



3) Salah satu repositori jnlua . Membantu saya memahami tujuan beberapa fungsi.

Source: https://habr.com/ru/post/id416079/


All Articles