加入Lua脚本

From Valve Developer Community
< Zh
Jump to: navigation, search
English (en)Deutsch (de)Русский (ru)中文 (zh)Translate (Translate)


小作品

这篇文章是一个小作品,您可以帮助我们完善它。

Lua 加入到起源引擎是十分简单的,而配置Lua的多个实例甚至更为简单。

源文件

将这两个源文件加入到你的工程里:

初始化Lua

当你完成添加文件的工作之后,你需要确保你的游戏代码能够恰当地调用初始化与终止的函数。在gameinterface.cpp文件中,于DLLInit函数的return true(第557行左右)前加入这些代码

#ifdef GE_LUA
	// Start LUA
	GELua()->InitDll();
#endif

然后在DLLShutdown函数前加入这些:

#ifdef GE_LUA
	// 终止Lua,关闭所有的游戏实例
	GELua()->ShutdownDll();
#endif

并且确保你在文件的最上方引用了管理器的头文件:

#ifdef GE_LUA
#include "ge_luamanager.h"
#endif

新建你的Lua实例

在这个有意思的部分,你将新建你自己的Lua实例。你需要新建一个类继承自LuaHandle,并且提供Init、Shutdown、RegFunctions和RegGlobals这四个方法。

Init

Init在Lua初始化之后被调用,这是加载你的脚本的绝佳时间。

void MyLuaHandle::Init()
{
	const char* luaFile = "myLuaFile.lua";

	// 载入到缓冲区内
	FileHandle_t f = filesystem->Open( luaFile, "rb", "MOD" );
	if (!f)
		return;

	// 将文件载入到null结尾的缓冲区内
	int fileSize = filesystem->Size(f);
	unsigned bufSize = ((IFileSystem *)filesystem)->GetOptimalReadSize( f, fileSize + 1 );

	char *buffer = (char*)((IFileSystem *)filesystem)->AllocOptimalReadBuffer( f, bufSize );
	Assert(buffer);

	((IFileSystem *)filesystem)->ReadEx( buffer, bufSize, fileSize, f ); // read into local buffer
	buffer[fileSize] = '\0'; // null terminate file as EOF
	filesystem->Close( f );	// close file after reading

	int error = luaL_loadbuffer( GetLua(), buffer, fileSize, luaFile );
	if (error)
	{
		Warning("[LUA-ERR] %s\n", lua_tostring(GetLua(), -1));
		lua_pop(GetLua(), 1);  /* 从栈中弹出错误信息 */
		Warning("[LUA-ERR] One or more errors occured while loading lua script!\n");
		return;
	}
	CallLUA(GetLua(), 0, LUA_MULTRET, 0, luaFile );
	m_bLuaLoaded = true;
}

Shutdown

Shutdown在Lua终止之时被调用,你可以在其中通知你的脚本去进行收尾工作。

RegGlobals

RegGlobals允许我们在Lua中注册全局变量。

void MyLuaHandle::RegGlobals()
{
	LG_DEFINE_INT("FOR_ALL_PLAYERS", -1);
	LG_DEFINE_INT("INVALID_ENTITY", -1);
	LG_DEFINE_INT("NULL", 0);
	LG_DEFINE_INT("GE_MAX_HEALTH", MAX_HEALTH);
	LG_DEFINE_INT("GE_MAX_ARMOR", MAX_ARMOR);
	LG_DEFINE_INT("MAX_PLAYERS", gpGlobals->maxClients);

	//Team Indices
	LG_DEFINE_INT("TEAM_NONE",TEAM_UNASSIGNED);
	LG_DEFINE_INT("TEAM_SPECTATOR",TEAM_SPECTATOR);
	LG_DEFINE_INT("TEAM_MI6",TEAM_MI6);
	LG_DEFINE_INT("TEAM_JANUS",TEAM_JANUS);

	//ClientPrintAll Types
	LG_DEFINE_INT("HUD_PRINTNOTIFY",HUD_PRINTNOTIFY);
	LG_DEFINE_INT("HUD_PRINTCONSOLE",HUD_PRINTCONSOLE);
	LG_DEFINE_INT("HUD_PRINTTALK",HUD_PRINTTALK);
	LG_DEFINE_INT("HUD_PRINTCENTER",HUD_PRINTCENTER);
}

同时宏提供了不同类型的版本。LG_DEFINE_INT用于int类型,LG_DEFINE_STRING用于字符串类型而LG_DEFINE_BOOL用于布尔类型。

RegFunctions

与RegGlobals类似,RegFunctions是为你注册想要暴露给Lua的C语言的类型的函数的地方。

void MyLuaHandle::RegFunctions()
{
	REG_FUNCTION( Msg );
	REG_FUNCTION( ConMsg );
	REG_FUNCTION( ClientPrintAll );
	REG_FUNCTION( GetTime );
}

这里又一次用到了宏来缩短代码的长度。之后你需要在另一个文件中定义这些函数。

extern "C"
{
	#include <lua.h>
	#include <lauxlib.h>
	#include <lualib.h>
}

int luaClientPrintAll(lua_State *L)
{
	int n = lua_gettop(L);    /* 参数的数量 */
	switch(n)
	{
	case 2:
		UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2));
		break;
	case 3:
		UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2),lua_tostring(L,3));
		break;
	case 4:
		UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2),lua_tostring(L,3),lua_tostring(L,4));
		break;
	case 5:
		UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2),lua_tostring(L,3),lua_tostring(L,4),lua_tostring(L,5));
		break;
	case 6:
		UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2),lua_tostring(L,3),lua_tostring(L,4),lua_tostring(L,5),lua_tostring(L,6));
		break;
	}
	return 0;
}

int luaMsg(lua_State *L)
{
	Msg("%s\n",lua_tostring(L,1));
	return 0;
}

int luaConMsg(lua_State *L)
{
	return luaMsg(L);
}

int luaGetTime(lua_State *L)
{
	lua_pushnumber( L, gpGlobals->curtime );
	return 1;
}
Note.png注意:这些函数的名字需为之前在宏中定义的名字加上lua前缀。

创建实例

在你的LuaHandle类的文件的顶部,你需要新添能够访问你的Lua单例的函数。像是这样:

MyLuaHandle* g_LuaHandle = NULL;
MyLuaHandle *GetLuaHandle()
{
	return g_LuaHandle;
}

之后在你的新LuaHandle类的构造函数中加入将自己设为那个单例的代码:

MyLuaHandle::MyLuaHandle() : LuaHandle()
{
	g_LuaHandle = this;
	Register();
}
Note.png注意:在构造函数的那里调用Register很重要,否则它或许不会正常工作!

最终在你自己的GameRules类的构造函数里创建一个新的LuaHandle:

CGameRules::CGameRules()
{

	//other stuff here

	// Start our LUA GamePlay Engine
	if (!GetLuaHandle())
		new MyLuaHandle();
}

工程设置

在你的服务器工程设置中你需要将GE_LUA宏添加到预处理器选项中(配置属性 -> c++ -> 预处理器 -> 预处理器定义)(原文中为(Configure properties -> c++ -> Preprocesser -> Preprocesser defines) 以及你需要把lua的库(应与库文件的名字相同,我的为lua5.1.lib)添加到链接器设置中(Configure properties -> Linker -> Input -> Additional Dependencies )

Note.png注意:确保你在Debug和Release的配置中都做了相同的步骤

现在把Lua库的文件放入src/lib文件夹中。你就应该能够编译并且愉悦地使用Lua了。

在c中调用Lua代码

这有点难,但你应该能够参考Lua的文档来找到解决方法,这里有一个简单的例子:

void CGELUAGamePlay::PostRoundBegin()
{
	if (m_bLuaLoaded)
	{
		lua_getglobal(GetLua(), "RoundStart");
		CallLUA(GetLua(), 0, 0, 0, "RoundStart");
	}
}