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

猜你喜欢

转载自blog.csdn.net/bull521/article/details/79944703