cocos2dxでluaプロジェクトでの苦労

## 開発環境について

まずはじめに、luaプロジェクトの最初の関門である。
構文に癖。

そこで、自分が使っている開発環境について
* エディタ
    * atom
* 入れたプラグイン
    * language-lua
    * linter
    * linter-lua

linter-luaを動かすために、
brewでlua,luajitをいれてlinter-luaの設定にパスを記載しました。

linter-luaこれがものすごく便利で、動的言語を開発するときはlintは必須だなとあらためて感じました。

lint入れる前の開発は悲惨で、やっぱりc++プロジェクトに戻そうかとなんども思ったことか。

いまだと逆にluaの構文に慣れてきたので他の言語でもthenを間違えて書いてしまう。



## luaで呼び出せないメソッド

次につまるところが、luaでの書き方も慣れてきて
動きを実装しているところで、assertが。。


callback系でこんな記載があるのがポロポロと

```cpp
// Lambda binding for lua is not supported.
assert(false);
```

特にanimationのsetLastFrameCallFuncが使えないのが
辛いです。

前も記事では、平気でlua_cocos2dx_auto.cppとかいじっていたけど、
ここはその名のとおり自動生成するものだから魔改造するにも触ってはいけなっかった。。


## 正しい方法だと思うこと

まずは自動生成する対象からメソッドを外して、manualに記載するのが正しいぽい。
http://qiita.com/giginet/items/6dd0c2e178ec8ef44927
この記事を見て気がつきました。。

## 実践!!!!

今回のsetLastFrameCallFuncを使いたい場合は

```ini:tools/tolua/cocos2dx_studio.ini
--- a/tools/tolua/cocos2dx_studio.ini
+++ b/tools/tolua/cocos2dx_studio.ini
@@ -53,7 +53,7 @@ skip =  *::[^visit$ copyWith.* onEnter.* onExit.* ^description$ getObjectType .*
         ActionObject::[initWithDictionary initWithBinary],
         BaseData::[copy subtract],
         ActionTimelineCache::[getInstance loadActionTimelineFromXML loadAnimationWithDataBuffer],
-        ActionTimeline::[setFrameEventCallFunc]
+        ActionTimeline::[setFrameEventCallFunc setLastFrameCallFunc]
 
 rename_functions =  ActionManagerEx::[shareManager=getInstance purgeActionManager=destroyInstance],
                     SceneReader::[purgeSceneReader=destroyInstance]
```

実行できるように準備は上のリンクを参考に
準備しておいていざ実行


```
cd tools/tolua
./genbindings.sh
```

これでmanualのほうにsetLastFrameCallFuncを定義する。

```cpp:cocos/scripting/lua-bindings/manual/cocostudio/lua_cocos2dx_coco_studio_manual.cpp
--- a/cocos/scripting/lua-bindings/manual/cocostudio/lua_cocos2dx_coco_studio_manual.cpp
+++ b/cocos/scripting/lua-bindings/manual/cocostudio/lua_cocos2dx_coco_studio_manual.cpp
@@ -503,6 +503,57 @@ tolua_lerror:
     return 0;
 }
 
+static int lua_cocos2dx_ActionTimeline_setLastFrameCallFunc(lua_State* L)
+{
+    if (nullptr == L)
+        return 0;
+    
+    int argc = 0;
+    cocostudio::timeline::ActionTimeline* self = nullptr;
+    
+#if COCOS2D_DEBUG >= 1
+    tolua_Error tolua_err;
+       if (!tolua_isusertype(L,1,"ccs.ActionTimeline",0,&tolua_err)) goto tolua_lerror;
+#endif
+    
+    self = static_cast<cocostudio::timeline::ActionTimeline*>(tolua_tousertype(L,1,0));
+    
+#if COCOS2D_DEBUG >= 1
+       if (nullptr == self) {
+               tolua_error(L,"invalid 'self' in function 'lua_cocos2dx_ActionTimeline_setLastFrameCallFunc'\n", NULL);
+               return 0;
+       }
+#endif
+    argc = lua_gettop(L) - 1;
+    
+    if (1 == argc)
+    {
+#if COCOS2D_DEBUG >= 1
+        if (!toluafix_isfunction(L,2,"LUA_FUNCTION",0,&tolua_err) )
+        {
+            goto tolua_lerror;
+        }
+#endif
+        
+        LUA_FUNCTION handler = (  toluafix_ref_function(L,2,0));
+        self->setLastFrameCallFunc([=](void){
+            LuaEngine::getInstance()->getLuaStack()->executeFunctionByHandler(handler, 0);
+        });
+        
+        return 0;
+    }
+    
+    
+    luaL_error(L, "'setLastFrameCallFunc' function of ActionTimeline has wrong number of arguments: %d, was expecting %d\n", argc, 1);
+    
+#if COCOS2D_DEBUG >= 1
+tolua_lerror:
+    tolua_error(L,"#ferror in function 'setLastFrameCallFunc'.",&tolua_err);
+#endif
+    return 0;
+}
+
+
 static void extendActionTimeline(lua_State* L)
 {
     lua_pushstring(L, "ccs.ActionTimeline");
@@ -510,6 +561,7 @@ static void extendActionTimeline(lua_State* L)
     if (lua_istable(L,-1))
     {
         tolua_function(L, "setFrameEventCallFunc", lua_cocos2dx_ActionTimeline_setFrameEventCallFunc);
+        tolua_function(L, "setLastFrameCallFunc", lua_cocos2dx_ActionTimeline_setLastFrameCallFunc);
     }
     lua_pop(L, 1);
 }
```

こんな感じでつかえるようになる。

```
local animation = cc.CSLoader:createNode("Animation.csb")
local action = cc.CSLoader:createTimeline("Animation.csb")
animation:runAction(action)

local playName = "stay"
action:play(playName, false)

action:setLastFrameCallFunc(function()
    if playName == "stay" then
        playName = "loop"
        action:play(playName, true)
    end
end)

```


## 3.9になって

CHANGELOGを見ていただけるとわかるように、
ActionTimelineにいろいろと便利なメソッドがつかされました。

```CHANGELOG
[NEW] Animate: Added ActionTimeline::setAnimationEndCallBack and ActionTimeline::addFrameEndCallFunc.
```
callback系なのでluaサポートを書いてくれていないです。。
追加するとき最初からluaもサポートしてほしい。

これも同じようにして追加すれば使えます。
特にsetAnimationEndCallFuncこれが便利でanimationNameを受け取れるので、
このanimationNameのあとに別のをplayとかが簡単に書けるようになるぽい。


## luaとは関係ないけど

cocos studioでguiがいじれるのでデザイナーとの連携が
楽になった。
アニメーションとかとくに。
しかし、CocosProject.ccsがcocos studioを開くだけで
変更されるのをなんとかしてほしい。
よくコンフリクトがおこりつらい。

あと、cocos stuiodでできるcsbてFlatBuffersらしいので、
ゲームのオリジナルデータとかもcocos studioで生成してFlatBuffersに出力できると
すごく嬉しい前はcocos studioにあったような気がしたけど。
変化が激しくて覚えていない。。



## まとめ

だいたいluaサポートされているけれども、callbackあたりで
サポート外が意外とあって辛い。

cocos studioとの連携もluaできてハッピー。＋

実際にc++とluaのハイブリットアプリがほとんどなのかな。

3.9のreleaseで
http://discuss.cocos2d-x.org/t/cocos2d-x-v3-9-released/24948
ComponentLua::createがものすごく良さそうに見える。
より一層ハイブリットが進みそうな。。
というかこれがluaの持ち味をいかせられそう。