如何用C++作遊戲(3)

上一講我把Lua基本的棧規則講了一下,而後完善了一下個人CLuaFn類。讓它能夠支持任意參數數量和函數名稱的傳值。固然,這些功能是爲了今天這篇文章而鋪路的。程序員

我是一名(C++)程序員,因此在不少時候,不想過多的使用Lua的特性,由於我的感受,Lua的語法要比(C++)的更加靈活。而我更但願,在函數調用的某些習慣上,遵循一些(C++)的規則。函數

好了,廢話少說,咱們先來看一個類(頭文件)。假設咱們要把這個對象,傳輸給Lua進行調用。ui

#ifndef _TEST_H 
#define _TEST_H

class CTest 
{ 
public: 
        CTest(void); 
        ~CTest(void);

        char* GetData(); 
        void SetData(const char* pData);

private: 
        char m_szData[200]; 
}; 
#endif

這個類裏面有兩個函數,一個是GetData(),一個是SetData(),之因此這麼寫,我要讓Lua不只能使用個人類,還能夠給這個類使用參數。lua

那麼,cpp文件,咱們姑且這樣寫。(固然,你能夠進行修改,按照你喜歡的方式寫一個方法)指針

char* CTest::GetData() 
{ 
        printf(「[CTest::GetData]%s./n」, m_szData); 
        return m_szData; 
}

void CTest::SetData(const char* pData) 
{ 
        sprintf(m_szData, 「%s」, pData); 
}

這是一個標準的類,我須要這個類在Lua裏面能夠創造出來,並賦予數值,甚至我能夠把CTest做爲一個Lua函數參數,傳給Lua函數讓它去給我 處理。讓咱們來看看怎麼作。若是使用標準的Lua語法,有點多,因此我就借用一下上次提到的tolua來作到這一切,我一句句的解釋。姑且咱們把這些代碼 放在LuaFn.cpp裏面。code

static int tolua_new_CTest(lua_State* pState) 
{ 
        CTest* pTest = new CTest(); 
        tolua_pushusertype(pState, pTest, 「CTest」); 
        return 1; 
}

static int tolua_delete_CTest(lua_State* pState) 
{ 
        CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0); 
        if(NULL != pTest) 
        { 
                delete pTest; 
        } 
        return 1; 
}

static int tolua_SetData_CTest(lua_State* pState) 
{ 
        CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0); 
        const char* pData = tolua_tostring(pState, 2, 0);

        if(pData != NULL && pTest != NULL) 
        { 
                pTest->SetData(pData); 
        }

        return 1; 
}

static int tolua_GetData_CTest(lua_State* pState) 
{ 
        CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);

        if(pTest != NULL) 
        { 
                char* pData = pTest->GetData(); 
                tolua_pushstring(pState, pData); 
        }

        return 1; 
}

看看這幾個靜態函數在幹什麼。對象

我要在Lua裏面使用CTest,必須讓Lua裏這個CTest對象可以順利的創造和銷燬。tolua_new_CTest()和tolua_delete_CTest()就是幹這個的。繼承

tolua_pushusertype(pState, pTest, 「CTest」);

這句話的意思是,將一個已經在Lua註冊的」CTest」對象指針,壓入數據棧,用指針形式彈出來。 。接口

,CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);是將數據棧下的對象以(CTest* )

tolua_SetData_CTest() 函數和tolua_GetData_CTest分別對應CTest的SetData方法和GetData()方法。開發

由於咱們的SetData方法裏面存在 變量,那麼一樣,咱們須要使用const char* pData = tolua_tostring(pState, 2, 0);將參數彈出來,而後輸入到pTest->SetData(pData);對象中去,固然,你能夠有更多若干個參數。隨你的喜愛。這裏只作一個 舉例。

好了,你必定會問,這麼多的靜態函數,用在哪裏?呵呵,固然是給Lua註冊,當你把這些數據註冊到Lua裏面,你就能夠輕鬆的在Lua中使用它們。

讓咱們看看,註冊是怎麼作到的。

仍是在CLuaFn類裏面,咱們增長一個函數。好比叫作bool InitClass();

bool CLuaFn::InitClass() 
{ 
        if(NULL == m_pState) 
        { 
                printf(「[CLuaFn::InitClass]m_pState is NULL./n」); 
                return false; 
        }

        tolua_open(m_pState); 
        tolua_module(m_pState, NULL, 0); 
        tolua_beginmodule(m_pState, NULL);

        tolua_usertype(m_pState, 「CTest」); 
        tolua_cclass(m_pState, 「CTest」, 「CTest」, 「」, tolua_delete_CTest);

        tolua_beginmodule(m_pState, 「CTest」); 
        tolua_function(m_pState, 「new」, tolua_new_CTest); 
        tolua_function(m_pState, 「SetData」, tolua_SetData_CTest); 
        tolua_function(m_pState, 「GetData」, tolua_GetData_CTest); 
        tolua_endmodule(m_pState);

        tolua_endmodule(m_pState);

        return true; 
}

上面的代碼,就是我把上面的幾個靜態函數,綁定到Lua的基礎對象中去。

tolua_beginmodule(m_pState, 「CTest」);是隻註冊一個模塊,好比,咱們管CTest叫作」CTest」,保持和C的名稱同樣。這樣在Lua的對象庫中就會多了一個 CTest的對象描述,等同於string,number等等基本類型,同理,你也能夠用一樣的方法,註冊你的MFC類。是否是有點明白了?這裏要注 意,tolua_beginmodule()和tolua_endmodule()對象必須成對出現,若是出現不成對的,你註冊的C類型將會失敗。

tolua_function(m_pState, 「SetData」, tolua_SetData_CTest);

指的是將Lua裏面CTest對象的」SetData」綁定到你的tolua_SetData_CTest()函數中去。

好的,讓咱們來點激動人心的東西。還記得咱們的Simple.lua的文件麼。咱們來改一下它。

function func_Add(x, y) 
  local test = CTest:new(); 
  test:SetData(「I’m freeeyes!」); 
  test:GetData(); 
  return x..y; 
end

我在這個函數裏面,New了一個CTest對象,並進行賦值操做,最後把結果打印在屏幕上。

你或許會問,最後一句不是x+y麼,怎麼變成了 x..y,呵呵,在Lua中,..表示聯合的意思,就比如在C++裏面, string strName += 「freeeyes」。原來以爲x+y有點土,索性返回一個兩個字符串的聯合吧。

好了,咱們已經把咱們的這個CTest類註冊到了Lua裏面,讓咱們來調用一下吧。修改一下Main函數。變成如下的樣子。

int _tmain(int argc, _TCHAR* argv[]) 
{ 
        CLuaFn LuaFn;

        LuaFn.InitClass();

        LuaFn.LoadLuaFile(「Sample.lua」);

        CParamGroup ParamIn; 
        CParamGroup ParamOut;

        char szData1[20] = {‘/0′}; 
        sprintf(szData1, 「[freeeyes]「); 
        _ParamData* pParam1 = new _ParamData(szData1, 「string」, (int)strlen(szData1)); 
        ParamIn.Push(pParam1);

        char szData2[20] = {‘/0′}; 
        sprintf(szData2, 「[shiqiang]「); 
        _ParamData* pParam2 = new _ParamData(szData2, 「string」, (int)strlen(szData2)); 
        ParamIn.Push(pParam2); 
        char szData3[40] = {‘/0′}; 
        _ParamData* pParam3 = new _ParamData(szData3, 「string」, 40); 
        ParamOut.Push(pParam3);

        LuaFn.CallFileFn(「func_Add」, ParamIn, ParamOut);

        char* pData = (char* )ParamOut.GetParam(0)->GetParam(); 
        printf(「[Main]Sum = %s./n」, pData);

        getchar();

        return 0; 
}

若是你徹底按照個人,你就能夠編譯你的工程了,運行一下,看看是啥結果?

[CTest::GetData]I’m freeeyes!. 
[Main]Sum = [freeeyes][shiqiang].

看看,是否是和我輸出的同樣?

呵呵,有意思吧,你已經能夠在Lua裏面用C++的函數了,那麼我們再增長一點難度,好比,我有一個CTest對象,要做爲一個參數,傳輸給func_Add()執行,怎麼辦?

很簡單,若是你對上面的代碼仔細閱讀,你會發現下面的代碼同樣簡潔。爲了支持剛纔要說的需求,咱們須要把Sample.lua再作一點修改。

function func_Add(x, y, f) 
  f:SetData(「I’m freeeyes!」); 
  f:GetData(); 
  return x..y; 
end

f假設就是咱們要傳入的CTest對象。咱們要在Lua裏面使用它。(咱們的CLuaFn都不用改,把main函數稍微改一下便可,來看看怎麼寫。)

// LuaSample.cpp : 定義控制檯應用程序的入口點。 
//

#include 「stdafx.h」 
#include 「LuaFn.h」

int _tmain(int argc, _TCHAR* argv[]) 
{ 
        CLuaFn LuaFn;

        LuaFn.InitClass();

        LuaFn.LoadLuaFile(「Sample.lua」);

        CParamGroup ParamIn; 
        CParamGroup ParamOut;

        char szData1[20] = {‘/0′}; 
        sprintf(szData1, 「[freeeyes]「); 
        _ParamData* pParam1 = new _ParamData(szData1, 「string」, (int)strlen(szData1)); 
        ParamIn.Push(pParam1);

        char szData2[20] = {‘/0′}; 
        sprintf(szData2, 「[shiqiang]「); 
        _ParamData* pParam2 = new _ParamData(szData2, 「string」, (int)strlen(szData2)); 
        ParamIn.Push(pParam2);

        //只追加了這裏 
        CTest* pTest = new CTest(); 
        _ParamData* pParam3 = new _ParamData(pTest, 「CTest」, sizeof(CTest)); 
        ParamIn.Push(pParam3); 
       //追加結束 
        char szData4[40] = {‘/0′}; 
        _ParamData* pParam4 = new _ParamData(szData4, 「string」, 40); 
        ParamOut.Push(pParam4);

        LuaFn.CallFileFn(「func_Add」, ParamIn, ParamOut);

        char* pData = (char* )ParamOut.GetParam(0)->GetParam(); 
        printf(「[Main]Sum = %s./n」, pData);

        getchar();

        return 0; 
}

好了,就這麼點代碼,改好了,咱們再Build一下,而後點擊運行。看看輸出結果,是否是和之前的同樣?

恩,是否是有點興奮了?你成功的讓Lua開始調用你的C++對象了!而且按照你要的方式執行!還記得我曾在第一篇文章裏面許諾過,我會讓你畫出一個MFC窗體麼?呵呵,若是你到如今依然以爲很清晰的話,說明你的距離已經不遠了。

既然已經到了這裏,咱們索性再加點難度,若是我要把CTest做爲一個對象返回回來怎麼作?很簡單,且看。

int _tmain(int argc, _TCHAR* argv[]) 
{ 
        CLuaFn LuaFn;

        LuaFn.InitClass();

        LuaFn.LoadLuaFile(「Sample.lua」);

        CParamGroup ParamIn; 
        CParamGroup ParamOut;

        char szData1[20] = {‘/0′}; 
        sprintf(szData1, 「[freeeyes]「); 
        _ParamData* pParam1 = new _ParamData(szData1, 「string」, (int)strlen(szData1)); 
        ParamIn.Push(pParam1);

        char szData2[20] = {‘/0′}; 
        sprintf(szData2, 「[shiqiang]「); 
        _ParamData* pParam2 = new _ParamData(szData2, 「string」, (int)strlen(szData2)); 
        ParamIn.Push(pParam2);

        CTest* pTest = new CTest(); 
        _ParamData* pParam3 = new _ParamData(pTest, 「CTest」, sizeof(CTest)); 
        ParamIn.Push(pParam3); 
        CTest* pTestRsult = NULL; 
        _ParamData* pParam4 = new _ParamData(pTestRsult, 「CTest」, sizeof(pTestRsult)); 
        ParamOut.Push(pParam4);

        LuaFn.CallFileFn(「func_Add」, ParamIn, ParamOut);

        //接受Lua返回參數爲CTest類型,並調用其中的方法。 
        pTestRsult = (CTest* )ParamOut.GetParam(0)->GetParam(); 
        pTestRsult->GetData();

        getchar();

        return 0; 
}

好,編譯,執行。呵呵,看到了吧。

看到這裏,若是你能看的明白,說明你已經對Lua如何調用C接口,以及C如何調用Lua有了必定的理解。固然,我寫的這個類也不是很完善,不過作一半的Lua開發,應該是夠用了。以以上的方式,你可使用Lua駕馭你的C++代碼。

好了,我們既然已經說到這裏了,再深一步,若是個人類是繼承的,怎麼辦?

好比,個人CTest繼承了一個CBase,個人CBase又繼承了一個。。。

在Lua裏面,同樣簡單,我拿MFC的例子來舉例吧,想必你們更喜歡看。

好比 CCmdTarget繼承自CObject。

那麼我在註冊的時候能夠這麼寫。

tolua_cclass(tolua_S, 「CCmdTarget」,      」CCmdTarget」,      」CObject」,            NULL);

這個表示CCmdTarget繼承自CObject對象。

固然,MFC裏面還會有不少類型,好比常數,Lua同樣能處理。

舉個例子說。

tolua_constant(tolua_S, 「ES_AUTOHSCROLL」,   ES_AUTOHSCROLL);

這樣註冊,你就能夠在 Lua裏面使用ES_AUTOHSCROLL這個常數,它會自動綁定ES_AUTOHSCROLL這個(C++)常數對象。

小夥伴們,還請持續關注更新,更多幹貨和資料請直接聯繫我,也能夠加羣710520381,邀請碼:柳貓,歡迎你們共同討論

相關文章
相關標籤/搜索