2

onPickup、onDrop、onUse などの多くの関数を持つ Entity というクラスがあります。これらの関数をすべて定義するスクリプトを作成し、C++ 関数から呼び出せるようにする必要があります。したがって、C++ で定義された関数は、何らかの機能を持つ対応する Lua 関数を呼び出すだけです。

しかし、ここに問題があります。プログラム内のすべてのエンティティが独自のスコープで動作するように、私が作成するすべてのスクリプトが必要です。

私は LuaBind を使用していますが、Lua の使用経験がないため、ここで少し迷っています。

4

3 に答える 3

3

私は lua bind を使用していませんが、これが役立つ場合があります。アイデアは、C++ クラスに lua 関数を登録し、C++ クラスで lua 関数への参照を保持することです。

C/C++ から呼び出し可能な lua 関数を定義するには、luaL_ref を使用して、コールバック関数への参照を C++ オブジェクトに格納します。

// a little helper function
template <typename T>
T *Lua_getUserData(lua_State *L) {
    assert(lua_isuserdata(L, 1) == 1);
    T **v = (T **) lua_touserdata(L, 1);
    assert(v != NULL);
    return *v;
}

int lua_FormRegisterMethods(lua_State *L) {
    Entity *f = Lua_getUserData<Entity>(L);
    assert(lua_istable(L, 2) == 1); // check the next parameter is a table
    lua_pushvalue(L,2); // dup the table
    f->LuaTable = luaL_ref(L, LUA_REGISTRYINDEX); // keep a reference to the table
    lua_getmetatable(L, 2); // get the metatable
    lua_pushstring(L, "OnClick"); 
    lua_rawget(L, -2); // get the OnClick Lua Function
    f->LuaMethod = luaL_ref(L, LUA_REGISTRYINDEX); // save a reference to it
    return 0;
}

その後、C++ イベントで lua メソッドを取得できます

lua_rawgeti( LuaInstance->L, LUA_REGISTRYINDEX, LuaMethod );
assert(lua_isfunction(LuaInstance->L, -1) == 1);

これで、前に保存したテーブルに設定された self でこの関数を呼び出すことができます。h番目

于 2011-04-29T12:37:17.590 に答える
1

たとえば、Lua関数を呼び出すことができます

int callLuaFunction(lua_State* lua_state) {
    return luabind::call_function<int>(lua_state, "myluafunction", param1);
}

Lua 関数が int を返し、1 つのパラメーターを取る場合。

好きなだけ を作ることができると確信してlua_Stateいます。エンティティの正しいものを に渡すだけcall_functionです。

于 2011-04-29T12:40:18.763 に答える
0

これを完全に実装するには、おそらく、Luaのより難解なビットのいくつかを少し掘り下げる必要があります。しかし、それは時間の価値があります。私がこれをどのように処理したかについて、非常に縮小されたバージョンを示します。注意してください、ここではすべてが一緒に機能する小さなビットがたくさんあります-主に保存された関数の保存と呼び出し、およびLuaユーザーデータとしてのc++オブジェクトの使用。

まず、単純なintであるlua参照としてイベントハンドラー(lua関数)を格納するc++クラスが必要です。ここでは配列を使用していますが、意味のあるものなら何でも使用できます。ここで起こっている主なことは、int参照によって参照されるlua関数を呼び出せるようにしたいということです。

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include <string>
#include <iostream>
#include <assert.h>
using namespace std;

enum enum_event_types {
    ON_USE, ON_DROP, ON_WHATEVER, EVENT_COUNT
};

class Entity {
  private:  
    int events[EVENT_COUNT];
  public: 
    lua_State* lua;
    void setEventHandler(int event, int ref) {
        assert(event < EVENT_COUNT);
        events[event] = ref;
    }
    void callEventHandler(int event) {
        int error;
        assert(event < EVENT_COUNT);
            // to call the function we need to get it from the registry index
        lua_rawgeti(lua, LUA_REGISTRYINDEX, events[event]);
        error = lua_pcall(lua, 0, 0, 0); // use protected call for errors
        if (error) {
            printf("error: %s", lua_tostring(lua, -1));
            lua_pop(lua, 1); 
        }
    }
};

次に、EntityクラスをLuaに公開します。これがどのように行われるかをよく知らない場合は、ここに良い記事があります。基本的に何が起こっているのかというと、Entity.new()から返されたユーザーデータをポインターへのポインターに設定しているということです。これは、Luaがオブジェクトをガベージコレクションしないようにするためです。次に、Luaに公開されるすべてのメソッドを保持する「Entity」のメタテーブルを作成します。

int L_newEntity(lua_State* L) {
    Entity **e = (Entity **)lua_newuserdata(L, sizeof(Entity *));
    *e = new Entity(); 
    (*e)->lua = L;
    lua_getglobal(L, "Entity");
    lua_setmetatable(L, -2); 
    return 1;
}

int L_setOnUse(lua_State* L) {
    Entity** e = (Entity**) lua_touserdata(L, 1);
    lua_pushvalue(L, 2);
    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
    (*e)->setEventHandler(ON_USE, ref);
    return 0;
}

// this will be exposed to Lua as a table called Entity
static const luaL_Reg L_entityMethods[] = {
    {"new", L_newEntity},{"setOnUse", L_setOnUse},{NULL, NULL}
};

次に、Lua状態を設定し、エンティティテーブルを作成してテストを作成します。テストはエンティティを作成し、そのイベントハンドラーを渡されたLua関数に設定します。最後に、Lua関数がc++オブジェクトによって呼び出されていることをテストします。

int main() {
    Entity** e;
    int error;
    lua_State* L=lua_open();
    luaL_openlibs(L);
    luaL_register(L, "Entity", L_entityMethods); 
    lua_pushvalue(L,-1);
    lua_setfield(L, -2, "__index"); 
    lua_pop(L, 1);

    luaL_loadstring(L, 
    "e = Entity.new(); "
    "e:setOnUse(function()"
    "   print('Some of them want to use you')"
    "end);");
    error = lua_pcall(L, 0, 0, 0);
    if (error) {
        printf("error: %s", lua_tostring(L, -1));
        lua_pop(L, 1); /* errors must be popped from stack */
    }   
    lua_getglobal(L, "e");
    if (lua_isuserdata(L, 1)) {
        e = (Entity**) lua_touserdata(L, 1);
        (*e)->callEventHandler(ON_USE);
    }
    return 0;
}

これは完全にはほど遠いです。まず、イベントハンドラーを2回設定する必要がある場合は、luaL_unrefを使用して最初に古い参照をクリアする必要があります。次に、発生したイベントに関するデータをイベントハンドラーに渡します。現在のイベントハンドラーはデータを取得しないため、APIのユーザーはほとんど先に進むことができません。おそらく、少なくともイベントを呼び出しているオブジェクトへの参照を渡す必要があります。これを使用して、Luaで非常に強力で使用可能なApiを作成できます。幸運を!

于 2011-04-29T19:36:41.380 に答える