LoginSignup
24
23

More than 5 years have passed since last update.

【Cocos2d-x】Script Bindingの作り方(その1)

Last updated at Posted at 2014-12-03

はじめに

@tkyajiと申します。Advent Calendar初参加です。
Cocos2d-xに、Script bindingを追加する際の実装方法を書いていきたいと思います。
Cocos2d-xのバージョンは3.2を使っています。

なお、bindings-generatorを使用する際に必要となる準備は、昨年のAdvent Calendarで
giginetさんがとてもわかりやすい記事を書いてくれているので、
その辺りの説明は省きます(助かった)
【cocos2d-x 3.0】binding-generatorでScript Bindingを自動化する方法

以下の説明の中で、追加するスクリプト言語を"Lang"と記述しています。
例えばLuaなら"Lang"は"Lua"に読み替えてください。

おおまなか手順

  1. ScriptEngineProtocolを継承したLangEngineクラスを作る
  2. C++の型とLang言語の型を変換するための関数を書く
  3. bindings-generatorにターゲットとなる言語を追加
  4. bindings-generatorで、グルーコード生成ロジックをひたすら書いていく
  5. 一通りできたら、cocosコマンドに対応する

だいたいこんな感じです。bindings-generatorとcocosコマンドの実装はPythonで書きます。
cocosコマンドに対応するのはおそらく最後になるので、まずはLua Bindingのプロジェクトを作り、
その中で実装していくのが良いと思います。

LangEngineクラスを作る

Luaの場合はLuaEngine、JSの場合はScriptingCoreにあたるEngineクラスを作成します。
cocos2d::ScriptEngineProtocolを継承し、以下のvirtual関数をオーバーライドします。

  • getScriptType

スクリプト言語ごとの定数を返却します。
ScriptEngineProtocolccScriptTypeというenumがあるので、ここにkScriptTypeLang定数を 追加し、
それを返却します。

  • removeScriptObjectByObject

Refクラスのデストラクタから呼ばれます。
autorelease等でC++上のインスタンスが死ぬ際に、Lang言語上のインスタンスも消す、
というような処理を実装します。
なお、Refクラスには_ID, _scriptObjectというフィールドがあり、
ここにLang言語上のインスタンス情報を設定することで、C++のインスタンスとLang言語のインスタンスとを
紐づけることができます。

  • executeString

指定したLang言語の文字列を読み込んで実行します。

  • executeScriptFile

指定したLang言語のスクリプトファイルを読み込んで実行します。
基本的にはこのメソッドでsrcディレクトリ配下のスクリプトファイルを実行していくので、
最初に実装が必要なメソッドになります。

executeGlobalFunction

指定した名前のLang言語上のグローバル関数を実行します。

sendEvent

各種イベント(メニュボタン押下、タッチ、スケジュール等々)発火時に呼び出されます。
引数のScriptEventでイベントの種類を判定します。
C++とは違う方法でイベント登録をしている場合は、このメソッドの中で登録した処理を呼び出します。
例えばLuaではregisterScriptTapHandlerというメソッドでコールバックを登録していますので、
この場合、sendEventの中でregisterScriptTapHandlerで登録したLuaの関数を実行します。
逆に言うと、C++と同様にEventDispatcherにイベント登録している場合は、
sendEventでは実装不要となります。

handleAssert

CCASERTから呼び出されます。ここではエラーログの出力と、Lang言語のエラー処理を実装します。
例えばLang言語上で例外を発生させる等です。

parseConfig

CocoStudioとの連携用?

C++ ⇔ Lang言語間の型変換関数を実装

Luaの場合はLuaBasicConversions, JSの場合はjs_manual_conversionsにあたるソースです。
C++の型と、Lang言語の型を変換する関数を作っていきます。
例えばLuaの場合、Vec2を変換する関数を以下のように定義しています。

// c++ -> lua
void vec2_to_luaval(lua_State* L,const cocos2d::Vec2& vec2)
{
    if (NULL  == L)
        return;
    lua_newtable(L);                                    /* L: table */
    lua_pushstring(L, "x");                             /* L: table key */
    lua_pushnumber(L, (lua_Number) vec2.x);               /* L: table key value*/
    lua_rawset(L, -3);                                  /* table[key] = value, L: table */
    lua_pushstring(L, "y");                             /* L: table key */
    lua_pushnumber(L, (lua_Number) vec2.y);               /* L: table key value*/
    lua_rawset(L, -3);
}

// lua -> c++
bool luaval_to_vec2(lua_State* L,int lo,cocos2d::Vec2* outValue)
{
    if (nullptr == L || nullptr == outValue)
        return false;

    bool ok = true;

    tolua_Error tolua_err;
    if (!tolua_istable(L, lo, 0, &tolua_err) )
    {
#if COCOS2D_DEBUG >=1
        luaval_to_native_err(L,"#ferror:",&tolua_err);
#endif
        ok = false;
    }

    if (ok)
    {
        lua_pushstring(L, "x");
        lua_gettable(L, lo);
        outValue->x = lua_isnil(L, -1) ? 0 : lua_tonumber(L, -1);
        lua_pop(L, 1);

        lua_pushstring(L, "y");
        lua_gettable(L, lo);
        outValue->y = lua_isnil(L, -1) ? 0 : lua_tonumber(L, -1);
        lua_pop(L, 1);
    }
    return ok;
}

これらの変換関数は、bindings-generatorの実装時に使います。
bindings-generatorを実装しながら、必要になった型を随時追加すれば良いので、最初に全て作る必要はありません。
また、上記のような変換関数は作らないで実装することも可能ですが、大抵の場合は作った方が楽だと思います。

とりあえず、本日はここまでです。
明日はbindings-generatorの実装周りを説明したいと思います。

【Cocos2d-x】Script Bindingの作り方(その2)

24
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
23