Cocos2d-x之LUA脚本引擎深入分析

Cocos2d-x之LUA脚本引擎深入分析,第1张

概述 另:本章所用Cocos2d-x版本为: Cocos2d-2.0.2 http://cn.cocos2d-x.org/download          大家好,又是一周过去了,这一周忙的有点焦头烂额,除了工作照例每天加班到九点外,工具箱又做了大幅改进,新的论坛游戏兔子game2z也上线了,Cocos2d-x的学习时间被压缩的很少了,现在是凌晨一点零六分,看着妻子睡熟的样子,我也只能告诉自已,坚持

另:本章所用Cocos2d-x版本为:

Cocos2d-2.0.2

http://cn.cocos2d-x.org/download

大家好,又是一周过去了,这一周忙的有点焦头烂额,除了工作照例每天加班到九点外,工具箱又做了大幅改进,新的论坛游戏兔子game2z也上线了,Cocos2d-x的学习时间被压缩的很少了,现在是凌晨一点零六分,看着妻子睡熟的样子,我也只能告诉自已,坚持到底。

好了,不说废话,本周奉上一篇初级入门教程博文,Cocos2d-x中的LUA引导与入门。

做为惯例,一切都是以HelloWorld的样例为准。我们今天学习用LUA来完成一版HelloWorld。

大家既使没有看过我的“HelloWorld 深入分析”一文,想必也无数次运行过Cocos2d-x里的HelloCpp工程,对于运行的结果画面熟烂于心。我们回想一下,这个画面里有什么。嗯,一个背景图精灵,一个文字标签,一个关闭按钮。OK,咱们就做这么个东西。

首先,我们要知道LUA是个什么东西,至于官方怎么说可以百度去查,但我想告诉你的是LUA就是一种可以在不必修改C++代码的情况下实现逻辑处理的手段。稍微讲的再明白一点,就是你用指定语法写一些逻辑处理函数然后保存成文本格式,这个文件称为脚本文件,可以被游戏执行。经过若干年的发展,现在在LUA中写逻辑,除了调用注册到LUA的静态C函数外,也已经可以方便的访问到C++工程中的类的成员函数。这是游戏开发史上最重要的技术之一。其改变了很多设计方案,使游戏变的灵活强大而极具扩展性。

在Cocos2d-x中,有两个类来完成对于LUA脚本文件的处理。

1. ccluaEngine:LUA脚本引擎

2. ccScriptEngineManager:脚本引擎管理器。

ccluaEngine类的基类是一个接口类,叫做ccScriptEngineProtocol,它规定了所有LUA引擎的功能函数,它和ccScriptEngineManager都存放在libcocos2d下的script_support目录中的ccScriptSupport.h/cpp中。

首先我们来看一下ccScriptEngineProtocol:


[cpp] view plain copy classCC_DLLccScriptEngineProtocol:publicCCObject { public: //取得LUA的全局指针,所有的LUA函数都需要使用这个指针来做为参数进行调用。 virtuallua_State*getLuaState(voID)=0; //通过LUA脚本ID移除对应的CCObject virtualvoIDremoveCCObjectByID(intnLuaID)=0; //通过函数索引值移除对应的LUA函数。 voIDremoveLuaHandler(intnHandler)=0; //将一个目录中的LUA文件加入到LUA资源容器中。 voIDaddSearchPath(constchar*path)=0; //执行一段LUA代码 virtualintexecuteString(char*codes)=0; //执行一个LUA脚本文件。 intexecuteScriptfile(char*filename)=0; //调用一个全局函数。 intexecuteGlobalFunction(char*functionname)=0; //通过句柄调用函数多种形态。 //通过句柄调用函数,参数二为参数数量。 intexecuteFunctionByHandler(intnHandler,intnumArgs=0)=0; intexecuteFunctionWithIntegerData(intdata)=0; intexecuteFunctionWithfloatData(floatdata)=0; intexecuteFunctionWithBooleanData(booldata)=0; intexecuteFunctionWithCCObject(char*typename)=0; //将一个整数数值压栈做为参数。 intpushIntegerToluaStack(intdata)=0; //将一个浮点数值压栈做为参数。 intpushfloatToluaStack(//将一个布尔数值压栈做为参数。 intpushBooleanToluaStack(//将一个CCObject指针和类型名压栈做为参数。 intpushCCObjectToluaStack(CCObject*pObject,87); background-color:inherit; Font-weight:bold">char*typename)=0; //执行单点触屏事件 intexecutetouchEvent(inteventType,CCtouch*ptouch)=0; //执行多点触屏事件。 intexecutetouchesEvent( intexecuteSchedule(floatdt)=0; };

这个接口类的功能函数的具体实现,我们要参看ccluaEngine类。

现在我们打开ccluaEngine.h:

copy //加入lua的头文件,约定其中代码使用C风格 extern"C"{ #include"lua.h" } //相应的头文件。 #include"ccTypes.h" #include"cocoa/CCObject.h" #include"touch_dispatcher/CCtouch.h" #include"cocoa/CCSet.h" #include"base_nodes/CCNode.h" #include"script_support/ccScriptSupport.h" //使用Cocos2d命名空间 NS_CC_BEGIN //由ccScriptEngineProtocol派生的实际功能类。 classccluaEngine:publicccScriptEngineProtocol //析构 ~ccluaEngine(); //取得LUA的全局指针,所有的LUA函数都需要使用这个指针来做为参数进行调用。 voID){ returnm_state; …此处省略若干字。 //加入一个多线程加载LUA脚本的实时回调函数,此函数用于ANDROID voIDaddLuaLoader(lua_CFunctionfunc); //取得当前单件实例指针 staticccluaEngine*engine(); private: //构造,单例,你懂的。 ccluaEngine(voID) :m_state(NulL) { //初始化函数。 boolinit(voID); //将一个句柄压栈 boolpushFunctionByHandler(intnHandler); //唯一的LUA指针 lua_State*m_state; }; NS_CC_END
分析其CPP实现:

copy //本类的头文件。 #include"ccluaEngine.h" //这里用到了tolua++库,tolua++库是一个专门处理LUA脚本的第三方库,可以很好的完成LUA访问C++类及成员函数的功能。如果没有tolua++,这块要处理起来可是麻烦死了。 #include"tolua++.h" //加入lua库的相应头文件。 extern"C"{ #include"lualib.h" #include"lauxlib.h" #include"tolua_fix.h" } //加入Cocos2d-x所用的相应头文件。 #include"cocos2d.h" #include"LuaCocos2d.h" #include"cocoa/CCArray.h" #include"CCScheduler.h" //如果是ANDROID平台,加上对多线程加载LUA脚本的支持,使用相应的头文件。 #if(CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID) #include"Cocos2dxLuaLoader.h" #endif //开始Cocos2d-x命名空间。 NS_CC_BEGIN //析构。 ccluaEngine::~ccluaEngine() //结束对LUA指针的使用,关闭LUA。 lua_close(m_state); //初始始。 boolccluaEngine::init(voID) //开始对LUA的使用,创建一个LUA指针。 m_state=lua_open(); //打开相应的库。 luaL_openlibs(m_state); //打开使用tolua封装的访问Cocos2d-x的库。 tolua_Cocos2d_open(m_state); tolua_prepare_ccobject_table(m_state); //如果是ANDROID平台,也加上对LUA进行多线程加载的库支持。 #if(CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID) addLuaLoader(loader_AndroID); #endif returntrue; //取得单例指针。 ccluaEngine*ccluaEngine::engine() ccluaEngine*pEngine=newccluaEngine(); pEngine->init(); pEngine->autorelease(); returnpEngine; voIDccluaEngine::removeCCObjectByID(intnLuaID) tolua_remove_ccobject_by_refID(m_state,nLuaID); //<span>通过函数索引值移除对应的LUA函数。</span> voIDccluaEngine::removeLuaHandler(intnHandler) tolua_remove_function_by_refID(m_state,nHandler); voIDccluaEngine::addSearchPath(char*path) //取得全局表package lua_getglobal(m_state,"package"); //取得其中的path字段,压入栈顶。 lua_getfIEld(m_state,-1,"path"); //取得当前的目录字符串。 char*cur_path=lua_tostring(m_state,-1); //参数出栈,恢复堆栈。 lua_pop(m_state,1); //将新路径字符串加入到路径串列中,压入栈顶。 lua_pushfstring(m_state,"%s;%s/?.lua",cur_path,path); //设置path字段值路径 lua_setfIEld(m_state,-2,0); background-color:inherit">//执行一段LUA代码 intccluaEngine::executeString(char*codes) //执行一段LUA代码。返回值存放到nRet中。 intnRet=luaL_dostring(m_state,codes); //进行下拉圾收集。 lua_gc(m_state,LUA_GCColLECT,0); //如果出错,打印日志。 if(nRet!=0) cclOG("[LUAERROR]%s",lua_tostring(m_state,-1)); lua_pop(m_state,1); returnnRet; return0; intccluaEngine::executeScriptfile(char*filename) //执行一个LUA脚本文件。返回值存放到nRet中。 intnRet=luaL_dofile(m_state,filename); //lua_gc(m_state,0); //调用一个全局函数。 intccluaEngine::executeGlobalFunction(char*functionname) //将全局函数放在栈顶 /*queryfunctionbyname,stack:function*/ //判断是否是函数。 if(!lua_isfunction(m_state,-1)) cclOG("[LUAERROR]name'%s'doesnotrepresentaLuafunction",functionname); //调用函数。 interror=lua_pcall(m_state,1,0);/*callfunction,stack:ret*/ if(error) //cleanerrormessage //getreturnvalue //如果取得的第一个参数不是数字,返回错误。 if(!lua_isnumber(m_state,-1)) //取得数字的参数存放在ret中。 intret=lua_tointeger(m_state,-1); //参数出栈,恢复堆栈。 /*stack:-*/ returnret; intccluaEngine::executeFunctionByHandler(intnumArgs) if(pushFunctionByHandler(nHandler)) if(numArgs>0) lua_insert(m_state,-(numArgs+1));/*stack:...funcarg1arg2...*/ interror=0; //try //{ error=lua_pcall(m_state,numArgs,0);/*stack:...ret*/ //} //catch(exception&e) //cclOG("[LUAERROR]lua_pcall(%d)catchC++exception:%s",nHandler,e.what()); //lua_settop(m_state,0); //return0; //catch(...) //cclOG("[LUAERROR]lua_pcall(%d)catchC++unkNownexception.",nHandler); lua_settop(m_state,0); intret=0; //如果返回参数是数字转为整数。 if(lua_isnumber(m_state,108); List-style:decimal-leading-zero outsIDe; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> ret=lua_tointeger(m_state,-1); }//如果是布尔型转为true或false elseif(lua_isboolean(m_state,108); List-style:decimal-leading-zero outsIDe; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> ret=lua_toboolean(m_state,153); background-color:inherit; Font-weight:bold">else intccluaEngine::executeFunctionWithIntegerData(intdata) lua_pushinteger(m_state,data); returnexecuteFunctionByHandler(nHandler,1); intccluaEngine::executeFunctionWithfloatData(floatdata) lua_pushnumber(m_state,参数二为布尔型数据。 intccluaEngine::executeFunctionWithBooleanData(booldata) lua_pushboolean(m_state,参数二为CCObject指针数据和其类型名称。 intccluaEngine::executeFunctionWithCCObject(char*typename) tolua_pushusertype_ccobject(m_state,pObject->m_uID,&pObject->m_nLuaID,pObject,typename); intccluaEngine::pushIntegerToluaStack(//将整数值压入堆栈 lua_pushinteger(m_state,data); //返回参数的数量。 returnlua_gettop(m_state); intccluaEngine::pushfloatToluaStack(//将数字值压入堆栈 lua_pushnumber(m_state,87); background-color:inherit; Font-weight:bold">intccluaEngine::pushBooleanToluaStack(//将boolean值压入堆栈 lua_pushboolean(m_state,87); background-color:inherit; Font-weight:bold">intccluaEngine::pushCCObjectToluaStack(CCObject*pObject,87); background-color:inherit; Font-weight:bold">intccluaEngine::executetouchEvent(CCPointpt=CCDirector::sharedDirector()->convertToGL(ptouch->getLocationInVIEw()); //将参数压栈后调用函数。 //执行多点触屏事件。 intccluaEngine::executetouchesEvent( //创建一个表 lua_newtable(m_state); //将多个触点信息参数放入表中。 CCDirector*pDirector=CCDirector::sharedDirector(); CCSetIteratorit=ptouches->begin(); CCtouch*ptouch; intn=1; while(it!=ptouches->end()) ptouch=(CCtouch*)*it; CCPointpt=pDirector->convertToGL(ptouch->getLocationInVIEw()); //将位置x压入堆栈 //将栈顶的数值放入到表中对应索引n的数值中 lua_rawseti(m_state,n++); ++it; //以表做为第二参数压栈,调用函数。 intccluaEngine::executeSchedule(floatdt) returnexecuteFunctionWithfloatData(nHandler,dt); //加入一个多线程加载LUA脚本的实时回调函数,此函数用于ANDROID voIDccluaEngine::addLuaLoader(lua_CFunctionfunc) if(!func)return; //取得全局表 //取得全局表中的“loaders”表 "loaders"); //将设定的函数和参数压栈 lua_pushcfunction(m_state,func); //将参数压栈 for(inti=lua_objlen(m_state,-2)+1;i>2;--i) //取得原"loaders"表第i-1个参数 lua_rawgeti(m_state,i-1); //将取出的值放到新"loaders"表中第i个数值 //将函数设为新"loaders"表的第2个参数 lua_rawseti(m_state,2); //把“loaders”表放到全局表中 boolccluaEngine::pushFunctionByHandler(intnHandler) //找出注册函数表的第nHandler个数值 lua_rawgeti(m_state,LUA_REGISTRYINDEX,nHandler);/*stack:...func*/ cclOG("[LUAERROR]functionrefID'%d'doesnotreferenceaLuafunction",nHandler); false; }
然后我们来看一下ccScriptEngineManager,这个类被称为脚本引擎管理器,其实很简单,只是用来设定当前项目的唯一正在使用的脚本引擎。也许Cocos2d-x打算用它管理多种类型脚本引擎,比如python,Js等。


copy classCC_DLLccScriptEngineManager ~ccScriptEngineManager(voID); //取得单例指针 ccScriptEngineProtocol*getScriptEngine(voID){ returnm_pScriptEngine; //设置使用的LUA管理器 voIDsetScriptEngine(ccScriptEngineProtocol*pScriptEngine); //移除使用的LUA管理器。 voIDremoveScriptEngine(staticccScriptEngineManager*sharedManager(//销毁单例 staticvoIDpurgeSharedManager( ccScriptEngineManager( :m_pScriptEngine(NulL) //使用的LUA脚本引擎 ccScriptEngineProtocol*m_pScriptEngine; 其对应的CPP实现: //全局唯一的 staticccScriptEngineManager*s_pSharedScriptEngineManager=NulL; //析构 ccScriptEngineManager::~ccScriptEngineManager( removeScriptEngine(); voIDccScriptEngineManager::setScriptEngine(ccScriptEngineProtocol*pScriptEngine) removeScriptEngine(); m_pScriptEngine=pScriptEngine; m_pScriptEngine->retain(); //移除使用的LUA管理器。 voIDccScriptEngineManager::removeScriptEngine(if(m_pScriptEngine) m_pScriptEngine->release(); m_pScriptEngine=NulL; //取得单例指针 ccScriptEngineManager*ccScriptEngineManager::sharedManager(if(!s_pSharedScriptEngineManager) s_pSharedScriptEngineManager=newccScriptEngineManager(); returns_pSharedScriptEngineManager; voIDccScriptEngineManager::purgeSharedManager(if(s_pSharedScriptEngineManager) deletes_pSharedScriptEngineManager; s_pSharedScriptEngineManager=NulL; 现在我们来实际 *** 作一下。

打开Hellolua工程中的AppDelegate.cpp:

在AppDelegate::applicationDIDFinishLaunching()函数中看这几行代码:

copy //取得LUA脚本引擎 ccScriptEngineProtocol*pEngine=ccluaEngine::engine(); //设置脚本引擎管理器使用新创建的LUA脚本引擎 ccScriptEngineManager::sharedManager()->setScriptEngine(pEngine); //如果是ANDROID平台,获hello.lua内存到字符串然后执行字符串 CCString*pstrfileContent=CCString::createWithContentsOffile("hello.lua"); if(pstrfileContent) pEngine->executeString(pstrfileContent->getCString()); #else //如果不是ANDROID平台,取得hello.lua文件全路径并执行文件。 std::stringpath=CCfileUtils::sharedfileUtils()->fullPathFromrelativePath("hello.lua"); pEngine->addSearchPath(path.substr(0,path.find_last_of("/")).c_str()); pEngine->executeScriptfile(path.c_str()); #endif
就这样,hello.lua中的脚本就可以被执行了。

现在我们将Hellolua工程目录拷出一份来,将目录和工程命名为StudyLua,并在程序运行目录中加入相关资源图片。之后我们打开hello.lua:


[HTML] copy --设置内存回收 collectgarbage("setpause",100) collectgarbage("setstepmul",5000) --取得窗口大小 localwinSize=CCDirector:sharedDirector():getWinSize() --将Hello背景图加入 localfunctioncreateLayerHello() locallayerHello=cclayer:create() --加入背景图 localbg=CCSprite:create("Hello.png") bg:setposition(winSize.wIDth/2,winSize.height/2) layerHello:addChild(bg) --创建HelloWorld locallabel=cclabelTTF:create("HelloCocos2d-x","Arial",50) label:setposition(winSize.wIDth/2,60) label:setVisible(true) layerHello:addChild(label) returnlayerHello end --将关闭按钮菜单加入 localfunctioncreateExitBtn() locallayerMenu=cclayer:create() --局部函数,用于退出 localfunctionmenuCallbackExit() CCDirector:sharedDirector():endTolua() --创建退出按钮 localmenuPopupItem=CcmenuItemImage:create("Closenormal.png","CloseSelected.png") --放在居上角附近 menuPopupItem:setposition(winSize.wIDth-50,winSize.height-50) --注册退出函数 menuPopupItem:registerScriptHandler(menuCallbackExit) --由菜单按钮项创建菜单 localmenuClose=Ccmenu:createWithItem(menuPopupItem) menuClose:setposition(0,0) menuClose:setVisible(true) --将菜单加入层中 layerMenu:addChild(menuClose) returnlayerMenu --创建场景 localsceneGame=CCScene:create() --将Hello背景图加入 sceneGame:addChild(createLayerHello()) sceneGame:addChild(createExitBtn()) --运行场景 CCDirector:sharedDirector():runWithScene(sceneGame)

运行一下:



我只能说,一切好极了,下课!

本文出自:http://blog.csdn.net/honghaIEr/article/details/8700574

总结

以上是内存溢出为你收集整理的Cocos2d-x之LUA脚本引擎深入分析全部内容,希望文章能够帮你解决Cocos2d-x之LUA脚本引擎深入分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1009625.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存