接下来,就可以注册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