对不起,很长的问题。如果您愿意,请跳过有关编译 Lua 的部分(几乎可以)并直接回答最后一个问题。
让我们像编译 Android 的静态库一样编译 Lua 库。
下载最新源代码并查看 doc/readme.html -在其他系统上构建 Lua部分以获取要编译的文件列表。
当然还要查看makefiles - 看看我们必须以随意的方式设置平台标志,例如linux,bsd等但是当然没有Android平台,所以我们可以选择将平台设置为ANSI,Linux,Posix或Generic。
第一个问题:即使没有任何平台标志,它也可以构建(除了关于 llex.c 的一个例外,我将在下面描述),所以这可能是不必要的吗?
我设置了 ANSI 标志。
安卓.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 中,现在所有错误都消失了。
现在只存在关于静态 int llex中“在case 结束时缺少中断”和“在函数中不返回非 void”的警告,但我们不再与 Lua 源代码混淆,因为它不是什么重要的。
第二个问题:我会为了这样的快速修复而去程序员地狱吗?
在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 脚本都有机会使整个应用程序崩溃(更多调用==更好的机会)。因此,一些播放器脚本有时也会在新的 pos 崩溃时更改其目录。
我认为这是因为 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);
为什么我使用双精度指针而不是双精度指针?好吧,它节省了 4 个字节的复制(每个指针的大小为 4,double 的大小为 8)frame_delta_time 参数到每个函数,如 update_ui(),我对每个大于 4 个字节的结构/类型做同样的事情,const 指针,而不仅仅是struct x 用于只读访问。这是坏事吗?