另:本章所用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:
- class CC_DLL CCScriptEngineProtocol : public CCObject
- {
- public:
-
- virtual lua_State* getLuaState(void) = 0;
-
-
- virtual void removeCCObjectByID(int nLuaID) = 0;
-
-
- virtual void removeLuaHandler(int nHandler) = 0;
-
-
- virtual void addSearchPath(const char* path) = 0;
-
-
- virtual int executeString(const char* codes) = 0;
-
-
- virtual int executeScriptFile(const char* filename) = 0;
-
-
- virtual int executeGlobalFunction(const char* functionName) = 0;
-
-
-
- virtual int executeFunctionByHandler(int nHandler, int numArgs = 0) = 0;
-
- virtual int executeFunctionWithIntegerData(int nHandler, int data) = 0;
-
- virtual int executeFunctionWithFloatData(int nHandler, float data) = 0;
-
- virtual int executeFunctionWithBooleanData(int nHandler, bool data) = 0;
-
- virtual int executeFunctionWithCCObject(int nHandler, CCObject* pObject, const char* typeName) = 0;
-
-
- virtual int pushIntegerToLuaStack(int data) = 0;
-
- virtual int pushFloatToLuaStack(int data) = 0;
-
- virtual int pushBooleanToLuaStack(int data) = 0;
-
- virtual int pushCCObjectToLuaStack(CCObject* pObject, const char* typeName) = 0;
-
-
- virtual int executeTouchEvent(int nHandler, int eventType, CCTouch *pTouch) = 0;
-
- virtual int executeTouchesEvent(int nHandler, int eventType, CCSet *pTouches) = 0;
-
- virtual int executeSchedule(int nHandler, float dt) = 0;
- };
这个接口类的功能函数的具体实现,我们要参看CCLuaEngine类。
现在我们打开CCLuaEngine.h:
-
- 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"
-
-
- NS_CC_BEGIN
-
-
-
- class CCLuaEngine : public CCScriptEngineProtocol
- {
- public:
-
- ~CCLuaEngine();
-
-
- virtual lua_State* getLuaState(void) {
- return m_state;
- }
-
- …此处省略若干字。
-
-
- virtual void addLuaLoader(lua_CFunction func);
-
- static CCLuaEngine* engine();
-
- private:
-
- CCLuaEngine(void)
- : m_state(NULL)
- {
- }
-
- bool init(void);
-
- bool pushFunctionByHandler(int nHandler);
-
- lua_State* m_state;
- };
-
- NS_CC_END
分析其CPP实现:
-
- #include "CCLuaEngine.h"
-
- #include "tolua++.h"
-
-
- extern "C" {
- #include "lualib.h"
- #include "lauxlib.h"
- #include "tolua_fix.h"
- }
-
-
- #include "cocos2d.h"
- #include "LuaCocos2d.h"
- #include "cocoa/CCArray.h"
- #include "CCScheduler.h"
-
-
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
- #include "Cocos2dxLuaLoader.h"
- #endif
-
-
- NS_CC_BEGIN
-
-
- CCLuaEngine::~CCLuaEngine()
- {
-
- lua_close(m_state);
- }
-
-
- bool CCLuaEngine::init(void)
- {
-
- m_state = lua_open();
-
- luaL_openlibs(m_state);
-
- tolua_Cocos2d_open(m_state);
- tolua_prepare_ccobject_table(m_state);
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
- addLuaLoader(loader_Android);
- #endif
- return true;
- }
-
-
- CCLuaEngine* CCLuaEngine::engine()
- {
- CCLuaEngine* pEngine = new CCLuaEngine();
- pEngine->init();
- pEngine->autorelease();
- return pEngine;
- }
-
-
- void CCLuaEngine::removeCCObjectByID(int nLuaID)
- {
- tolua_remove_ccobject_by_refid(m_state, nLuaID);
- }
-
- void CCLuaEngine::removeLuaHandler(int nHandler)
- {
- tolua_remove_function_by_refid(m_state, nHandler);
- }
-
- void CCLuaEngine::addSearchPath(const char* path)
- {
-
- lua_getglobal(m_state, "package");
-
- lua_getfield(m_state, -1, "path");
-
- const char* cur_path = lua_tostring(m_state, -1);
-
- lua_pop(m_state, 1);
-
- lua_pushfstring(m_state, "%s;%s/?.lua", cur_path, path);
-
- lua_setfield(m_state, -2, "path");
-
- lua_pop(m_state, 1);
- }
-
- int CCLuaEngine::executeString(const char *codes)
- {
-
- int nRet = luaL_dostring(m_state, codes);
-
- lua_gc(m_state, LUA_GCCOLLECT, 0);
-
- if (nRet != 0)
- {
- CCLOG("[LUA ERROR] %s", lua_tostring(m_state, -1));
- lua_pop(m_state, 1);
- return nRet;
- }
- return 0;
- }
-
-
- int CCLuaEngine::executeScriptFile(const char* filename)
- {
-
- int nRet = luaL_dofile(m_state, filename);
-
-
- if (nRet != 0)
- {
- CCLOG("[LUA ERROR] %s", lua_tostring(m_state, -1));
- lua_pop(m_state, 1);
- return nRet;
- }
- return 0;
- }
-
- int CCLuaEngine::executeGlobalFunction(const char* functionName)
- {
-
- lua_getglobal(m_state, functionName);
-
- if (!lua_isfunction(m_state, -1))
- {
- CCLOG("[LUA ERROR] name '%s' does not represent a Lua function", functionName);
- lua_pop(m_state, 1);
- return 0;
- }
-
- int error = lua_pcall(m_state, 0, 1, 0);
-
-
- if (error)
- {
- CCLOG("[LUA ERROR] %s", lua_tostring(m_state, - 1));
- lua_pop(m_state, 1);
- return 0;
- }
-
-
-
- if (!lua_isnumber(m_state, -1))
- {
- lua_pop(m_state, 1);
- return 0;
- }
-
- int ret = lua_tointeger(m_state, -1);
-
- lua_pop(m_state, 1);
- return ret;
- }
-
-
- int CCLuaEngine::executeFunctionByHandler(int nHandler, int numArgs)
- {
- if (pushFunctionByHandler(nHandler))
- {
- if (numArgs > 0)
- {
- lua_insert(m_state, -(numArgs + 1));
- }
-
- int error = 0;
-
-
- error = lua_pcall(m_state, numArgs, 1, 0);
-
-
-
-
-
-
-
-
-
-
-
-
-
- if (error)
- {
- CCLOG("[LUA ERROR] %s", lua_tostring(m_state, - 1));
- lua_settop(m_state, 0);
- return 0;
- }
-
-
- int ret = 0;
-
- if (lua_isnumber(m_state, -1))
- {
- ret = lua_tointeger(m_state, -1);
- }
- else if (lua_isboolean(m_state, -1))
- {
- ret = lua_toboolean(m_state, -1);
- }
-
- lua_pop(m_state, 1);
- return ret;
- }
- else
- {
- return 0;
- }
- }
-
-
- int CCLuaEngine::executeFunctionWithIntegerData(int nHandler, int data)
- {
- lua_pushinteger(m_state, data);
- return executeFunctionByHandler(nHandler, 1);
- }
-
- int CCLuaEngine::executeFunctionWithFloatData(int nHandler, float data)
- {
- lua_pushnumber(m_state, data);
- return executeFunctionByHandler(nHandler, 1);
- }
-
- int CCLuaEngine::executeFunctionWithBooleanData(int nHandler, bool data)
- {
- lua_pushboolean(m_state, data);
- return executeFunctionByHandler(nHandler, 1);
- }
-
- int CCLuaEngine::executeFunctionWithCCObject(int nHandler, CCObject* pObject, const char* typeName)
- {
- tolua_pushusertype_ccobject(m_state, pObject->m_uID, &pObject->m_nLuaID, pObject, typeName);
- return executeFunctionByHandler(nHandler, 1);
- }
-
- int CCLuaEngine::pushIntegerToLuaStack(int data)
- {
-
- lua_pushinteger(m_state, data);
-
- return lua_gettop(m_state);
- }
-
- int CCLuaEngine::pushFloatToLuaStack(int data)
- {
-
- lua_pushnumber(m_state, data);
-
- return lua_gettop(m_state);
- }
-
- int CCLuaEngine::pushBooleanToLuaStack(int data)
- {
-
- lua_pushboolean(m_state, data);
-
- return lua_gettop(m_state);
- }
-
- int CCLuaEngine::pushCCObjectToLuaStack(CCObject* pObject, const char* typeName)
- {
- tolua_pushusertype_ccobject(m_state, pObject->m_uID, &pObject->m_nLuaID, pObject, typeName);
- return lua_gettop(m_state);
- }
-
-
- int CCLuaEngine::executeTouchEvent(int nHandler, int eventType, CCTouch *pTouch)
- {
- CCPoint pt = CCDirector::sharedDirector()->convertToGL(pTouch->getLocationInView());
-
- lua_pushinteger(m_state, eventType);
- lua_pushnumber(m_state, pt.x);
- lua_pushnumber(m_state, pt.y);
- return executeFunctionByHandler(nHandler, 3);
- }
-
-
- int CCLuaEngine::executeTouchesEvent(int nHandler, int eventType, CCSet *pTouches)
- {
-
- lua_pushinteger(m_state, eventType);
-
- lua_newtable(m_state);
-
- CCDirector* pDirector = CCDirector::sharedDirector();
- CCSetIterator it = pTouches->begin();
- CCTouch* pTouch;
- int n = 1;
- while (it != pTouches->end())
- {
- pTouch = (CCTouch*)*it;
- CCPoint pt = pDirector->convertToGL(pTouch->getLocationInView());
-
- lua_pushnumber(m_state, pt.x);
-
- lua_rawseti(m_state, -2, n++);
-
- lua_pushnumber(m_state, pt.y);
-
- lua_rawseti(m_state, -2, n++);
- ++it;
- }
-
- return executeFunctionByHandler(nHandler, 2);
- }
-
- int CCLuaEngine::executeSchedule(int nHandler, float dt)
- {
- return executeFunctionWithFloatData(nHandler, dt);
- }
-
- void CCLuaEngine::addLuaLoader(lua_CFunction func)
- {
- if (!func) return;
-
-
- lua_getglobal(m_state, "package");
-
- lua_getfield(m_state, -1, "loaders");
-
- lua_pushcfunction(m_state, func);
-
- for (int i = lua_objlen(m_state, -2) + 1; i > 2; --i)
- {
-
- lua_rawgeti(m_state, -2, i - 1);
-
- lua_rawseti(m_state, -3, i);
- }
-
- lua_rawseti(m_state, -2, 2);
-
- lua_setfield(m_state, -2, "loaders");
-
- lua_pop(m_state, 1);
- }
-
- bool CCLuaEngine::pushFunctionByHandler(int nHandler)
- {
-
- lua_rawgeti(m_state, LUA_REGISTRYINDEX, nHandler);
-
- if (!lua_isfunction(m_state, -1))
- {
- CCLOG("[LUA ERROR] function refid '%d' does not reference a Lua function", nHandler);
- lua_pop(m_state, 1);
- return false;
- }
- return true;
- }
然后我们来看一下CCScriptEngineManager,这个类被称为脚本引擎管理器,其实很简单,只是用来设定当前项目的唯一正在使用的脚本引擎。也许Cocos2d-x打算用它管理多种类型脚本引擎,比如python,js等。
- class CC_DLL CCScriptEngineManager
- {
- public:
-
- ~CCScriptEngineManager(void);
-
- CCScriptEngineProtocol* getScriptEngine(void) {
- return m_pScriptEngine;
- }
-
- void setScriptEngine(CCScriptEngineProtocol *pScriptEngine);
-
- void removeScriptEngine(void);
-
- static CCScriptEngineManager* sharedManager(void);
-
- static void purgeSharedManager(void);
-
- private:
-
- CCScriptEngineManager(void)
- : m_pScriptEngine(NULL)
- {
- }
-
- CCScriptEngineProtocol *m_pScriptEngine;
- };
- 其对应的CPP实现:
-
- static CCScriptEngineManager* s_pSharedScriptEngineManager = NULL;
-
- CCScriptEngineManager::~CCScriptEngineManager(void)
- {
- removeScriptEngine();
- }
-
- void CCScriptEngineManager::setScriptEngine(CCScriptEngineProtocol *pScriptEngine)
- {
- removeScriptEngine();
- m_pScriptEngine = pScriptEngine;
- m_pScriptEngine->retain();
- }
-
- void CCScriptEngineManager::removeScriptEngine(void)
- {
- if (m_pScriptEngine)
- {
- m_pScriptEngine->release();
- m_pScriptEngine = NULL;
- }
- }
-
-
- CCScriptEngineManager* CCScriptEngineManager::sharedManager(void)
- {
- if (!s_pSharedScriptEngineManager)
- {
- s_pSharedScriptEngineManager = new CCScriptEngineManager();
- }
- return s_pSharedScriptEngineManager;
- }
-
- void CCScriptEngineManager::purgeSharedManager(void)
- {
- if (s_pSharedScriptEngineManager)
- {
- delete s_pSharedScriptEngineManager;
- s_pSharedScriptEngineManager = NULL;
- }
- }
现在我们来实际操作一下。
打开HelloLua工程中的AppDelegate.cpp:
在AppDelegate::applicationDidFinishLaunching()函数中看这几行代码:
-
- CCScriptEngineProtocol* pEngine = CCLuaEngine::engine();
-
- CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
- CCString* pstrFileContent = CCString::createWithContentsOfFile("hello.lua");
- if (pstrFileContent)
- {
- pEngine->executeString(pstrFileContent->getCString());
- }
- #else
-
- std::string path = 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)
-
-
- -- 取得窗口大小
- local winSize = CCDirector:sharedDirector():getWinSize()
- -- 将Hello背景图加入
-
- local function createLayerHello()
- local layerHello = CCLayer:create()
-
- -- 加入背景图
- local bg = CCSprite:create("Hello.png")
- bg:setPosition(winSize.width / 2 , winSize.height / 2)
- layerHello:addChild(bg)
-
- -- 创建HelloWorld
- local label = CCLabelTTF:create("Hello Cocos2d-x", "Arial", 50)
- label:setPosition(winSize.width / 2 ,60)
- label:setVisible(true)
- layerHello:addChild(label)
-
- return layerHello
- end
-
- --将关闭按钮菜单加入
- local function createExitBtn()
-
- local layerMenu = CCLayer:create()
-
- --局部函数,用于退出
- local function menuCallbackExit()
- CCDirector:sharedDirector():endToLua()
- end
-
- -- 创建退出按钮
- local menuPopupItem = CCMenuItemImage:create("CloseNormal.png", "CloseSelected.png")
- -- 放在居上角附近
- menuPopupItem:setPosition(winSize.width - 50, winSize.height - 50)
- -- 注册退出函数
- menuPopupItem:registerScriptHandler(menuCallbackExit)
- -- 由菜单按钮项创建菜单
- local menuClose = CCMenu:createWithItem(menuPopupItem)
- menuClose:setPosition(0, 0)
- menuClose:setVisible(true)
- -- 将菜单加入层中
- layerMenu:addChild(menuClose)
-
- return layerMenu
- end
-
- --创建场景
- local sceneGame = CCScene:create()
- --将Hello背景图加入
- sceneGame:addChild(createLayerHello())
- --将关闭按钮菜单加入
- sceneGame:addChild(createExitBtn())
- --运行场景
- CCDirector:sharedDirector():runWithScene(sceneGame)
运行一下:
我只能说,一切好极了,下课!
本文出自:http://blog.csdn.net/honghaier/article/details/8700574