RPG遊戲製作-08-為lua註冊c/c++函式
接下來,就可以註冊c函式給lua使用了。本遊戲中把不同種類的函式集合成各種各樣的模組,就類似於lua中的math模組io模組。目前的模組如下:
1.base模組,註冊了一些常用的基礎函式。
2.movement模組,移動函式,場景切換函式等。
3.screen模組,場景相關函式,如淡入淡出函式,調出揹包函式等。
4.timer模組,延遲函式。
void register_funcs(lua_State* pL) { static const luaL_Reg cpplibs[] = { {"base", open_base}, {"movement", open_movement}, {"screen", open_screen}, {"timer", open_timer}, {NULL, NULL} }; const luaL_Reg* lib = nullptr; for (lib = cpplibs;lib->func;lib++) { luaL_requiref(pL, lib->name, lib->func, 1); lua_pop(pL, 1); } }
這個函式類似於lua中的luaL_openlibs(),添加了全部的模組。詳細的請看帖子:
https://blog.csdn.net/bull521/article/details/79938211
然後就是一些模組內函式的編寫,由於目前函式較少,故全部放在了ScriptFunc.h ScriptFunc.cpp檔案中。
//------------------------------base------------------------------------ //一些基礎的函式及宣告 extern int open_base(lua_State* pL); //遊戲狀態 extern int getGameState(lua_State* pL); extern int setGameState(lua_State* pL);
每個模組都必有一個open_*()函式,來註冊相關的函式給lua。
int open_base(lua_State* pL) { const luaL_Reg baselib[] = { {"getGameState", getGameState}, {"setGameState", setGameState}, {NULL, NULL} }; luaL_newlib(pL, baselib); return 1; } int getGameState(lua_State* pL) { auto state = GameScene::getInstance()->getGameState(); lua_Integer nState = static_cast<lua_Integer>(state); lua_pushinteger(pL, nState); return 1; } int setGameState(lua_State* pL) { lua_Integer nState = luaL_checkinteger(pL, 1); GameState state = static_cast<GameState>(nState); GameScene::getInstance()->setGameState(state); return 0; }
遊戲狀態主要是了以後在進行指令碼狀態下,需要遮蔽各種事件的響應。
在lua中使用c/c++中的列舉體有兩種方法:
1.在c/c++中建立一個table,之後放入對應的鍵值對,如果有多個,重複上述操作。
2.在lua中根據列舉體的值直接建立。
我這裡使用的是第二種,第一種雖然能實現當列舉體的順序改變時,不需要修改列舉體註冊函式,但是當列舉體太多時,會有很多重複性程式碼。
//------------------------------movement------------------------------------
int open_movement(lua_State* pL)
{
const luaL_Reg movementlib[] = {
{"changeMap", changeMap},
{NULL, NULL}
};
luaL_newlib(pL, movementlib);
return 1;
}
int changeMap(lua_State* pL)
{
const char* mapName = luaL_checkstring(pL, 1);
float x = (float)luaL_checknumber(pL, 2);
float y = (float)luaL_checknumber(pL, 3);
GameScene::getInstance()->changeMap(mapName, Point(x, y));
return 0;
}
movement模組中目前僅有一個切換地圖函式。
//------------------------------screen------------------------------------
int open_screen(lua_State* pL)
{
const luaL_Reg screenlib[] = {
{"fadeInScreen", fadeInScreen},
{"fadeOutScreen", fadeOutScreen},
{NULL, NULL}
};
luaL_newlib(pL, screenlib);
return 1;
}
int fadeInScreen(lua_State* pL)
{
float duration = (float)luaL_checknumber(pL, 1);
auto gameScene = GameScene::getInstance();
auto scriptLayer = gameScene->getScriptLayer();
//設定等待時間
scriptLayer->setWaitType(WaitType::Time);
scriptLayer->setWaitTime(duration);
gameScene->getEffectLayer()->fadeInScreen(duration);
gameScene->yield(0);
return 0;
}
int fadeOutScreen(lua_State* pL)
{
float duration = (float)luaL_checknumber(pL, 1);
auto gameScene = GameScene::getInstance();
auto scriptLayer = gameScene->getScriptLayer();
//設定等待時間
scriptLayer->setWaitType(WaitType::Time);
scriptLayer->setWaitTime(duration);
gameScene->getEffectLayer()->fadeOutScreen(duration);
gameScene->yield(0);
return 0;
}
淡入淡出場景函式會阻塞對應指令碼的執行,即協程,這裡注意下yield的位置是在函式的末尾的,因為lua_yield()後的語句不會執行的,這個在lua wiki中有說明。
//------------------------------screen------------------------------------
int open_timer(lua_State* pL)
{
const luaL_Reg timerlib[] = {
{"delay", delay},
{NULL, NULL}
};
luaL_newlib(pL, timerlib);
return 1;
}
int delay(lua_State* pL)
{
float duration = (float)luaL_checknumber(pL, 1);
auto gameScene = GameScene::getInstance();
auto scriptLayer = gameScene->getScriptLayer();
//設定等待時間
scriptLayer->setWaitType(WaitType::Time);
scriptLayer->setWaitTime(duration);
gameScene->yield(0);
return 0;
}
程式碼同上,相比於淡入淡出場景只是少了一個動畫而已。
int GameScene::yield(int nresult)
{
return lua_yield(m_pLuaState, nresult);
}
簡單封裝了lua_yield()。其實可以直接在上面的函式中lua_yield(pL)的,因為pL就是GameScene中的m_pLuaState,這裡是為了保持一致性,故全部呼叫的GameScene中封裝的函式。
另外一個問題就是注意一下NPC的execute函式的修改:
bool NonPlayerCharacter::execute(int playerID)
{
auto gameScene = GameScene::getInstance();
int result = LUA_ERRRUN;
bool ret = false;
//獲得函式
gameScene->getLuaGlobal(m_name.c_str());
gameScene->getLuaField(-1, "execute");
//放入引數
gameScene->getLuaGlobal(m_name.c_str());
gameScene->pushInt(playerID);
//執行函式
result = gameScene->resumeFunction(2);
if (result == LUA_YIELD)
{
}
else if (result == LUA_OK)
{
//獲取返回值
ret = gameScene->toLuaBoolean(-1);
gameScene->pop(2);
}
int n = lua_gettop(gameScene->getLuaState());
return ret;
}
當lua_resume()返回的是LUA_YIELD時,此時的棧會被清空,也就是說不需要主動清空棧即可,而返回LUA_OK時,則需要主動清空棧的內容。
然後就是在lua檔案中新增內容了。
先建立一個rpg_core.lua,內部聲明瞭一些table來對映c/c++中的列舉體。
--包含著一些列舉體和一些函式
--遊戲狀態列舉體
GameState = {};
GameState.Normal = 0;
GameState.Fighting = 1;
GameState.Script = 2;
然後就是 "門"table:
--傳送門 傳送到1.tmx
Map01_02_Portal01 = NonPlayerCharacter:new();
function Map01_02_Portal01:execute(playerID)
base.setGameState(GameState.Script);
--淡入場景
screen.fadeInScreen(0.8);
--切換場景
movement.changeMap("map/1.tmx",26,23);
--淡出場景
screen.fadeOutScreen(0.8);
base.setGameState(GameState.Normal);
return true;
end