長い質問で申し訳ありません。必要に応じて、Lua のコンパイルに関する部分を飛ばして (ほとんど問題ありません)、最後の質問に直接進んでください。
Lua ライブラリを Android のスタティック ライブラリのようにコンパイルしてみましょう。
最新のソースをダウンロードし、doc/readme.html - Building Lua on other systemsセクションを調べて、コンパイルするファイルのリストを確認してください。
もちろん、makefile を調べてください。Linux、bsd などのプラットフォーム フラグを簡単に設定する必要があることを確認します。
最初の質問:プラットフォーム フラグがなくても問題なくビルドできるので (以下で説明する llex.c に関する 1 つの例外を除いて)、これは不要なのでしょうか?
ANSIフラグを設定しました。
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lua
LOCAL_CFLAGS := -DLUA_ANSI
LOCAL_SRC_FILES := lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c lmathlib.c loslib.c lstrlib.c ltablib.c loadlib.c linit.c
include $(BUILD_STATIC_LIBRARY)
アプリケーション.mk
APP_MODULES := lua
APP_PLATFORM := android-8
APP_OPTIM := release
APP_ABI := armeabi
そしてもちろんエラーが発生しました
Compile thumb : lua <= llex.c
jni/llex.c: In function 'trydecpoint':
jni/llex.c:214:18: error: 'struct lconv' has no member named 'decimal_point'
#if !defined(getlocaledecpoint)
#define getlocaledecpoint() (localeconv()->decimal_point[0]) //Missing struct member
#endif
最も安価な方法でそれを修正する
#if !defined(getlocaledecpoint)
#define getlocaledecpoint() ('.') //Code-monkey style
#endif
Android NDK のlocale.hにはいくつかの制限があるため、このエラーは驚くべきことではありません。
size_t、UCHAR_MAX、INT_MAX に関するエラーも発生しました - llimits.hインクルードを llex.c に追加すると、すべてのエラーがなくなりました。
static int llexの「ケースの最後にブレークがありません」と「非 void を返す関数に戻りがない」という警告のみが存在するようになりましたが、重要ではないため、Lua ソース コードをいじることはもうありません。
2 番目の質問:そのような迅速な修正のためにプログラマー地獄に行くつもりですか?
obj/armeabiディレクトリにある焼きたての LuaLib を取得して、テストします。もちろん、Android ファイル システムからスクリプトをロードするには、Java で AssetManager クラスを使用してファイル ローダーを作成する必要があるため、lua グローバル変数をプッシュして読み取ることで、はるかに簡単に実行しましょう。
TestLua - Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lua
LOCAL_SRC_FILES := liblua.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/lua-inc //Where .h files from lua src stored
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := LuaLibTest
LOCAL_STATIC_LIBRARIES:= lua
LOCAL_SRC_FILES := LuaLibTest.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
LuaLibTest.c
#include "LuaLibTest.h"
#include "lua-inc/lua.h"
#include "lua-inc/lauxlib.h"
#include <android/log.h>
#define INFO_TAG "[INFO]"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, INFO_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_lualib_test_NativeLib_testLua(JNIEnv* env, jclass _class)
{
LOGI("HI FROM C");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_pushstring(L, "Some string from Android C" );
lua_setglobal(L, "TEST" );
lua_getglobal(L, "TEST" );
const char* res = lua_tostring(L, lua_gettop(L));
LOGI("LUA TEST VAL: %s", res);
lua_pop(L, 1);
lua_close(L);
}
また、AssetManager でスクリプト ファイルを読み取り、その内容をlual_dostring()にフィードする文字列に入れる場合にも機能します。そうです、Android 用の lua をビルドします (ただし、lua i/o 関数は動作しません。printf のような stdio は Android NDK では機能しないためです)。
ただし、このビルドには奇妙なエラーがあります。たとえば、fpsスクリプトはフレームごとに更新されますが、フレームデルタ時間でfpsを更新する関数で次のエラーが発生すると、いつでも落ちる可能性があります
FPS.lua
FPS = {}
function initFPS()
FPS.fps = 0
FPS.last_fps = 0
FPS.frames_count = 0
FPS.frames_time = 0.0
local fps_msg = "FPS: " .. FPS.fps
c_set_fps(fps_msg);//Set some label in app - c function
end
function updateFPS(frameDeltaTime)
FPS.frames_count = FPS.frames_count + 1
FPS.frames_time = FPS.frames_time + frameDeltaTime
if FPS.frames_time >= 1000.0
then
FPS.frames_time = 0.0;
FPS.fps = FPS.frames_count;
FPS.frames_count = 0;
if FPS.last_fps ~= FPS.fps
then
local fps_msg = "FPS: " .. FPS.fps
c_set_fps(fps_msg);
FPS.last_fps = FPS.fps
end
end
end
FPS.c
void update_fps(const double* frame_delta_time) //SEGFAULT at this, at random time
{
lua_State* l = get_lua();
lua_getglobal(l, "updateFPS");
lua_pushnumber(l, *frame_delta_time);
lua_call(l, 1, 0);
}
そして、ランダムな時間 (1 分 - 3 分) でアプリ全体がクラッシュする次のエラー メッセージを取得します。
最後の質問 (よし、できた/退屈な部分は飛ばして)
セグメンテーション違反が発生するのはなぜですか? FPS スクリプトは単なる例です。せいぜいすべての lua スクリプトがアプリ全体をクラッシュさせる可能性があります (より多くの呼び出し == より良いチャンス)。そのため、新しい位置でディレクトリを変更する一部のプレーヤー スクリプトもクラッシュすることがあります。
Android/Java のガベージ クリーナーと Lua のガベージ クリーナーが競合しているためと思われるため、既に解放されているメモリを何かで解放しようとします。
編集 - そこからダブルポインターが来て、その理由:
#define MS_1_SEC 1000.0
typedef struct time_manager
{
double _time;
double delta_time;
}time_manager;
static double get_ms(s_time* time)//get time in ms
{
return MS_1_SEC * time->tv_sec + (double) time->tv_nsec / NS_1_SEC;
}
double get_time_now()
{
s_time time_now;
clock_gettime(CLOCK_REALTIME, &time_now);
return get_ms(&time_now);
}
void init_time_manager(time_manager* tm)
{
tm->_time = get_time_now();
tm->delta_time = 0.0;
}
void update_time_manager(time_manager* tm)
{
double time_now = get_time_now();
tm->delta_time = time_now - tm->_time;
tm->_time = time_now;
}
static time_manager TM;//Global static var for whole render module
onInit() 関数内
init_time_manager(&TM);
onDraw() 関数内
double* frame_time = &TM.delta_time;//get pointer to delta time
update_ui(frame_time);//Pass it every function
update_sprites(frame_time);
update_fps(frame_time);
...
draw_fps();
update_time_manager(&TM);
double ではなく double へのポインターを使用するのはなぜですか? 4 バイトのコピーを節約できます (すべてのポインターのサイズは 4、double のサイズは 8)。frame_delta_time パラメータを update_ui() などのすべての関数に渡します。読み取り専用アクセスの場合は struct x。これは悪いことですか?