程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++ 對象的Lua腳本化

C++ 對象的Lua腳本化

編輯:C++入門知識

腳本化編程 腳本化編程的最大好處就是簡單靈活,另外就是熱更新,這在網游中廣泛被采用,在網游中,通常采用引擎(c/C++)+腳本(lua/python)的架構,那種SDK性質的代碼放在引擎中,這些代碼在游戲上線後通常很穩定很少被修改,而真正游戲邏輯的制作就都在腳本層中進行。這樣有兩個好處:1.腳本層的bug基本不會導致程序的crash,因為是沙盒的。2.對於運行的代碼,可以方便的采用熱更新修復bug。 C與lua的交互   而如果想在腳本層編寫邏輯代碼,一個最重要的就是需要將引擎層即C++中的對象、函數、全局變量等等暴露給腳本層來訪問,當然有時也要能讓C++層能夠方便的訪問腳本層的全局變量。最近稍微仔細的探索了一下C++與Lua之間交互的知識,稍作總結,分享給需要的人。 在C中可以寫代碼訪問調用lua(函數、變量..),同時也可以在C中寫代碼讓所lua裡面能訪問c。c和lua的交互通常無外乎就以下這三種情況 \ 1.這種情況C是啟動程序,然後調用lua裡的變量 2.這種情況C被編成庫程序,啟動程序是lua(通常是你的lua解釋器lua.exe),在lua中可以使用c的變量 3.這種情況C是啟動程序,然後調用lua裡的變量,然後調用的lua裡面又能訪問c的變量。   無論以上哪種,情況,做到C和lua交互的代碼都是在c中寫的,這些代碼被稱為lua的C API。我們要利用C API做三件事:www.2cto.com 1.執行某個LUA腳本 2.獲取某個LUA的全局變量 2.將C變量或函數注冊成指定的規格,使得在LUA中可以調用注冊的那些C變量和函數。   如果能完成者三件事,那麼就可以完成上圖的所有這些途徑。 將C API的常用功能和交互的API以圖形化的方法繪制出來: 上圖中C和lua通過一個虛擬的棧(全局的還有函數內部局部的)來實現互相的訪問。 其中 1.執行某個LUA腳本 2.獲取某個LUA的全局變量 2.將C變量或函數注冊成指定的規格,使得在LUA中可以調用注冊的那些C變量和函數。 這三點都可以在上圖中找到相關的實現方法,但是這些方法都是為C寫的,對於C++的引擎來說,我們通常想實現一種在腳本層的面向對象的訪問,通俗來說,比如C++層有個類C,C有個借口 Get(),我們希望在腳本層可以方便的寫出c= newClassC(),c:Get()這樣的代碼,這就是本文探討的C++對象的LUa腳本化,這在C++引擎的腳本化編程中非常重要。 C++ 對象的Lua腳本化 假設有一個類MyCClass 我在C++層實現了它的一些方法,如SetI(int)、 GetI()等,我想將這個類腳本化給Lua層能面向對象的訪問。 下面寫下我的一些簡要實現 --New : 在C++中定義函數int MyCClass::NewMyCClass( lua_State* L ) ,這個函數在lua中可以使用c=NewMyCClass()來生成一個MyCClass的對象 NewMyCClass 具體實現是: int MyCClass::NewMyCClass( lua_State* L ) { //創建usrdata實例並傳到棧中  size_t bytes=sizeof(MyCClass);  MyCClass* c=(MyCClass*)lua_newuserdata(L,bytes);    //為usrdata創建元表以實現面向對象的方法,這裡給c這個對象加入一個元表,原表中對GetI這個index賦予MyCClass::GetI這個函數  int r=luaL_newmetatable(L,"MyCClassMeta");  lua_pushvalue(L,-1);//copy --index table  lua_pushcfunction(L,MyCClass::GetI);  lua_setfield(L,-2,"GetI");//reg func  lua_setfield(L,-2,"__index");//set __index table    lua_setmetatable(L,-2);//-2?  return 1; } 當然在程序開始處要注冊這個NewMyCClass lua_register(L,"NewMyCClass",MyCClass::NewMyCClass);   --成員函數GetI:可以在lua層 c:GetI() int MyCClass::GetI( lua_State* L ) {  MyCClass* c=(MyCClass*)lua_touserdata(L,1); int r=c->GetI()  lua_pushinteger(L,r);  return 1; }   --成員函數SetI  可以在lua層 c:SetI(100) int MyCClass::SetI( lua_State* L ) {  MyCClass* c=(MyCClass*)(lua_touserdata(L,1));  int p1=luaL_checkint(L,2); int r=c->SetI(p1)  return 0;   }   這樣一個非常簡單的在lua層面向對象的訪問C++的實現就好了。   優化 1.完整的版本在元表的賦值中可能還有加入MyCClass::Del以實現c:Del()來刪除 但是有些架構所有的析構操作可能都在引擎層完成,並不暴露給腳本層,(甚至new操作也是) 2.上面的代碼對於在c++中寫的每個類都要相應的寫出來一遍這些函數,如對於MyCClass::SetI(int i)你就要寫一個供lua調用版本的static int MyCClass::SetI( lua_State* L ),因為給lua調用函數永遠是這種類型,且需要是全局的。在實際框架的搭建中,我們通常采用宏函數的方法,如定義一個宏 REG_CLASS_FUNC(class,class_func)來按一定規則自動產生這個lua調用版的函數。相應的也可以通過定義一個宏函數REG_CLASS來自動注冊一個類的lua_register(L,"NewMyCClass",MyCClass::NewMyCClass)給lua

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved