現在,越來越多的C++服務器和客戶端融入了腳本的支持,尤其在網游領域,腳本語言已經滲透到了方方面面,比如你可以在你的客戶端增加一個腳本,這個腳本將會幫你在界面上顯示新的數據,亦或幫你完成某些任務,亦或幫你查看別的玩家或者NPC的狀態。。。如此等等。
但是我覺得,其實腳本語言與C++的結合,遠遠比你在游戲中看到的特效要來的迅猛。它可以運用到方方面面的領域,比如你最常見的應用領域。比如,你可以用文本編輯器,寫一個腳本語言,然后用你的程序加載一下,就會產生出很絢麗的界面。亦或一兩句文本語言,就會讓你的程序發送數據給服務器,是不是很酷呢?
本來我想,寫一篇關於主流腳本語言Lua和Python的文章,但是感覺這樣過於乏味,於是分開來一一介紹,相信對C++了解的你,看過我的文章后會對腳本語言這種東西產生濃厚的興趣,我想起以前聽的一個故事,當年Java的創造者講課的時候,一開始先拿一個簡單的不能簡單的小例子,不斷的擴展,最后成為一個復雜而完美的程序。今天我也就這樣實驗一下吧,呵呵。
當然,我本人不敢說對腳本語言了如指掌,只能說略微掌握一些,用過幾年,偏頗之處請大家指正。
下面,開始吧,先說LUA!(本文面向初學者)
Lua語言(http://www.lua.org/),想必不少程序員都聽過,據我所知,由於《魔獸世界》里面對它的加載,它一下子變成了很多游戲開發者競相研究的對象,至於這個巴西創造者么,我不過多介紹,大家有興趣可以谷歌一下。其實網上有很多關於lua的教材和例子,說真的,對於當年的我而言,幾乎看不懂,當時很郁悶,感覺Lua復雜的要命,有些懼怕,后來沉下心來一點點研究,覺得其實還是蠻簡潔的。只是網上的資料或許偏向於某些功能,導致了邏輯和代碼的復雜。后來總結,其實學習一種腳本語言,完全可以抱着放松的心態一點點的研究,反而效果會更好。
在講代碼之前,我要說Lua的一些特點,這些特點有利於你在復雜的代碼調用中,清晰的掌握中間的來龍去脈。實際上,你能常常用到的lua的API,不過超過10個,再復雜的邏輯。基本上也是這么多API組成的。至於它們是什么,下面的文章會介紹。另外一個重要之重要的概念,就是棧。Lua與別的語言交互以及交換數據,是通過棧完成的。其實簡單的解釋一下,你可以把棧想象成一個箱子,你要給他數據,就要按順序一個個的把數據放進去,當然,Lua執行完畢,可能會有結果返回給你,那么Lua還會利用你的箱子,一個個的繼續放下去。而你取出返回數據呢,要從箱子頂上取出,如果你想要獲得你的輸入參數呢?那也很簡單,按照頂上返回數據的個數,再按順序一個個的取出,就行了。不過這里提醒大家,關於棧的位置,永遠是相對的,比如-1代表的是當前棧頂,-2代表的是當前棧頂下一個數據的位置。棧是數據交換的地方,一定要有一些棧的概念。
好了,基礎的lua語法不在這里講,百度一下有很多。
先去http://www.lua.org/ 去下載一個最新的Lua代碼(現在穩定版是lua-5.1.4)。它的代碼是用C寫的,所以很容易兼容很多平台。
在linux下,目錄src下就有專門的Makefile。很簡單,啥都不用做,指定一下位置編譯即可。
在windows下,以VS2005為例,建立一個空的靜態庫工程(最好不使用預編譯頭,把預編譯頭的選項勾去掉),然后把src下的所有文件(除了Makefile)一股腦拷到工程中去。然后將這些文件添加到你的工程中,編譯,會生成一個*.llib(*是你起的lua庫名),行了,建立一個目錄lib,把它拷過去,然后再建立一個include的文件夾,把你工程目錄下的lua.h,lualib.h,lauxlib.h,拷貝過去。行了,拿着這兩個文件夾,你就可以在你的工程里使用lua了。
行了,材料齊了,我們來看看怎么寫一個簡單的lua程序吧。
(1)lua程序
(2)C++程序(頭文件)
extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" }; class CLuaFn { public: CLuaFn(void); ~CLuaFn(void); void Init(); //初始化Lua對象指針參數 void Close(); //關閉Lua對象指針 bool LoadLuaFile(const char* pFileName); //加載指定的Lua文件 bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2); //執行指定Lua文件中的函數 private: lua_State* m_pState; //這個是Lua的State對象指針,你可以一個Lua文件對應一個 };
(3)C++程序(.cpp文件)
#include "CLuaFn.h" CLuaFn::CLuaFn(void){m_pState = NULL;}; //這句干嘛 CLuaFn::~CLuaFn(void){}; //初始化函數 void CLuaFn::Init() { if (NULL == m_pState) { m_pState = lua_open(); //返回一個lua對象指針 luaL_openlibs(m_pState); //加載了所有你可能用到的Lua基本庫 } } //關閉Lua對象並釋放資源 void CLuaFn::Close() { if(NULL != m_pState) { lua_close(m_pState); m_pState = NULL; } } bool CLuaFn::LoadLuaFile(const char* pFileName) { int nRet = 0; if (NULL == m_pState) { printf("[CLuaFn::LoadLuaFile]m_pState is NULL./n"); return false; } //加載文件的時候盡量放在程序的初始化中 nRet = luaL_dofile(m_pState, pFileName); if (nRet != 0) { printf("[CLuaFn::LoadLuaFile]luaL_loadfile(%s) is file(%d)(%s)./n", pFileName, nRet, lua_tostring(m_pState, -1)); return false; } return true; } bool CLuaFn::CallFileFn(const char* pFunctionName, int nParam1, int nParam2) { int nRet = 0; if (NULL == m_pState) { printf("[CLuaFn::CallFileFn]m_pState is NULL./n"); return false; } //驗證你的Lua函數是否在你當前加載的Lua文件中,並把指針指向這個函數位置 lua_getglobal(m_pState, pFunctionName); // 壓棧(順序:從左到右的參數):第一個參數 lua_pushnumber(m_pState, nParam1); // 壓棧:第二個參數 lua_pushnumber(m_pState, nParam2); //執行這個函數,2是輸入參數的個數,1是返回值的個數 nRet = lua_pcall(m_pState, 2, 1, 0); if (nRet != 0) { printf("[CLuaFn::CallFileFn]call function(%s) error(%d)./n", pFunctionName, nRet); return false; } if (lua_isnumber(m_pState, -1) == 1) { int nSum = lua_tonumber(m_pState, -1); printf("[CLuaFn::CallFileFn]Sum = %d./n", nSum); } return true; }
(4)C++程序(main文件)
#include "CLuaFn.h" int main(int argc, char* argv[]) { CLuaFn LuaFn; LuaFn.Init(); LuaFn.LoadLuaFile("Sample.lua"); LuaFn.CallFileFn("func_Add", 11, 12); getchar(); return 0; }
注:程序中的注釋已經很詳盡,這里不在對代碼進行解釋。下載安裝lua之后,在vs2010中還要引入相應的庫。截圖如下:
至此,build成功之后運行,如果出現如下界面則表示成功!