另:本章所用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:
- classCC_DLLCCScriptEngineProtocol:publicCCObject
- {
- public:
-
- virtuallua_State*getLuaState(void)=0;
-
-
- 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:
//加入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实现:
//本类的头文件。
- #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);
- //<spanstyle="font-family:Arial,sans-serif;">通过函数索引值移除对应的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);
- 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));
- interror=0;
- //try
- //{
- error=lua_pcall(m_state,numArgs,0);
- //}
- //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);
- }
- 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);
- 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()函数中看这几行代码:
- 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:
--设置内存回收
- 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 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|