如何用C++做游戏(3)

上一讲我把Lua基本的栈规则讲了一下,然后完善了一下我的CLuaFn类。让它可以支持任意参数数量和函数名称的传值。当然,这些功能是为了今天这篇文章而铺路的。

我是一名C++程序员,所以在很多时候,不想过多的使用Lua的特性,因为个人感觉,Lua的语法要比C++的更加灵活。而我更希望,在函数调用的某些习惯上,遵循一些C++的规则。

好了,废话少说,我们先来看一个类(头文件)。假设我们要把这个对象,传输给Lua进行调用。

#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不仅能使用我的类,还可以给这个类使用参数。

那么,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里面。

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,邀请码:柳猫,欢迎大家共同讨论

柳猫
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,783评论 0 38
  • 上一节讲了一些基本的Lua应用,或许你会说,还是很简单么。呵呵,恩,是的,本来Lua就是为了让大家使用的方便快捷而...
    从梦流风阅读 576评论 0 0
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,397评论 0 17
  • 现在,越来越多的C++服务器和客户端融入了脚本的支持,尤其在网游领域,脚本语言已经渗透到了方方面面,比如你可以在你...
    从梦流风阅读 1,132评论 0 0
  • 想—— 笑今生,几壶浊酒 品命运,一杯淡茶 想—— 了无牵挂,在寒冷的野地里漫步 天马行空,在烈风的山顶上放歌 想...
    动远山阅读 284评论 1 1