[cocos2dx] lua註冊回撥到c++
思路
像所有語言一樣,繫結回撥主要是執行的任務執行到特定情形的時候,呼叫對用回撥方法。 這裡也一樣。核心思路是,當c程式碼執行到特定特定情形的時候,呼叫lua的方法
我這裡使用的是用lua_stack直接呼叫lua的方法,沒有使用cocos2dx封裝的那個dispatcher,因為熟悉那個格式太墨跡了
主要步驟如下
- 快取lua函式在lua環境中的引用
- 在c程式碼的地方用c的方式設定好回撥
- 在c程式碼回撥函式執行的時候,呼叫lua函式
實現
- c程式碼繫結回撥,呼叫lua函式
void ArmatureNode::registerMovementEventHandler(int handler)
{
unregisterMovementEventHandler(); //移除之前註冊的監聽
_movementHandler = handler; //快取lua函式的引用 這個後邊說
auto dispatcher = getCCEventDispatcher();
auto f = [this](cocos2d::EventCustom *event) //註冊c程式碼形式的回撥 這裡用function做
{
auto eventData = (dragonBones::EventData*)(event->getUserData());
auto type = (int) eventData->getType();
auto movementId = eventData->animationState->name;
auto lastState = eventData->armature->getAnimation()->getLastAnimationState();
auto stack = cocos2d::LuaEngine::getInstance()->getLuaStack();
stack->pushObject(this, "db.ArmatureNode");
stack ->pushInt(type);
stack->pushString(movementId.c_str(), movementId.size());
//通過LuaStack呼叫lua裡的函式 最後一個引數設定引數個數
stack->executeFunctionByHandler(_movementHandler, 3);
};
dispatcher->addCustomEventListener(dragonBones::EventData::COMPLETE, f);
}
void ArmatureNode::unregisterMovementEventHandler(void)
{
if (0 != _movementHandler)
{
cocos2d::LuaEngine::getInstance()->removeScriptHandler(_movementHandler); //移除lua函式的繫結
_movementHandler = 0;
}
}
- 提供lua函式繫結到c的方法
上邊的這個函式直接用cocos裡的genbinding.py 是無法正確生成lua裡可呼叫的介面的,需要手動編寫繫結方法
說這個得用到cocos2dx中提供的一個方法 toluafix_ref_function 會把一個lua棧中的方法轉成一個int,以便c++中呼叫。我會在最後面說這個
int tolua_db_DBCCArmature_registerMovementEventHandler(lua_State* tolua_S)
{
if (NULL == tolua_S)
return 0;
int argc = 0;
dragonBones::ArmatureNode* self = nullptr;
self = static_cast<dragonBones::ArmatureNode*>(tolua_tousertype(tolua_S,1,0)); //第一個引數 就是lua裡的self
argc = lua_gettop(tolua_S) - 1;
if (1 == argc)
{
//第二個引數,就是lua裡的function 這裡要通過toluafix_ref_function這個函式對映成一個Int值
int handler = (toluafix_ref_function(tolua_S,2,0));
self->registerMovementEventHandler(handler);
return 0;
}
return 0;
}
- 將繫結方法繫結到lua環境裡
int extends_ArmatureNode(lua_State* tolua_S)
{
lua_pushstring(tolua_S, "db.ArmatureNode");//之前db.ArmatureNode是通過指令碼繫結在lua裡。這裡只做擴充套件
lua_rawget(tolua_S, LUA_REGISTRYINDEX);
if (lua_istable(tolua_S,-1))
{
lua_pushstring(tolua_S,"registerMovementEventHandler");
lua_pushcfunction(tolua_S,tolua_db_DBCCArmature_registerMovementEventHandler);
lua_rawset(tolua_S,-3);
}
lua_pop(tolua_S, 1);
return 0;
}
- lua裡設定回撥到c++
local arm = db.ArmatureNode:create("Dragon")
local animation = arm:getAnimation()
animation:gotoAndPlay("walk")
arm:registerMovementEventHandler(
function(...)
print(...)
end
)
-測試
列印回撥輸出,測試通過 userdata 8 walk
其他
- toluafix_ref_function 以及 toluafix_get_function_by_refid
這兩個方法是相互對應的 toluafix_ref_function這個方法在登錄檔上將一個lua的function與一個function_id生成對映 toluafix_get_function_by_refid 方法可以通過前一個方法生成的function_id來講繫結的lua function放到棧頂
//
TOLUA_API int toluafix_ref_function(lua_State* L, int lo, int def)
{
if (!lua_isfunction(L, lo)) return 0;
s_function_ref_id++; //function_id 加1
lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING);//在登錄檔上,存放luafunction 對映table 的key壓棧
lua_rawget(L, LUA_REGISTRYINDEX); //獲取方法對映表,放在棧頂
lua_pushinteger(L, s_function_ref_id); //function_id壓棧
lua_pushvalue(L, lo); //lo有效處索引處是lua方法,lua方法拷貝,壓棧
lua_rawset(L, -3); //生成對映
lua_pop(L, 1);
return s_function_ref_id;
}
TOLUA_API void toluafix_get_function_by_refid(lua_State* L, int refid)
{
lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING); //存放luafunction 對映table 的key壓棧
lua_rawget(L, LUA_REGISTRYINDEX); //獲取方法對映表,放在棧頂
lua_pushinteger(L, refid); //function_id壓棧
lua_rawget(L, -2); //獲取到的luafunction 放到棧頂
lua_remove(L, -2); //
}
-
executeFunctionByHandler
executeFunctionByHandler 這個方法只是通過toluafix_get_function_by_refid 獲取到function 然後通過lua_pcall 方法呼叫 程式碼就不寫了
疑惑
包括cocos2dx裡的所有lua擴充套件(不是通過指令碼直接生成lua介面的)都是通過登錄檔裡擴充套件的 lua_rawget(tolua_S, LUA_REGISTRYINDEX);
我沒完全看完lua裡的userdata繫結過程,封裝的太深了。
疑惑是綁定了以後也是userdata,但是擴充套件的時候拿到都是table。按我已看到的程式碼是userdata繫結到global表裡的。這裡的實現機制怎麼回事,知道的,望不吝指點
轉載請註明出處(http://www.cnblogs.com/boliu/p/4091274.html)