[Cocos2d-x]Lua 資源熱更新

什麼是熱更新

所謂的熱更新,指的是客戶端的更新。php

大體的流程是,客戶端在啓動後訪問更新的URL接口,根據更新接口的反饋,下載更新資源,而後使用新的資源啓動客戶端,或者直接使用新資源不重啓客戶端。ios

 

熱更新代碼使用到的場景

  • 情人節快到了,你想要組織一個遊戲內活動,錯過期機確定是你最不想要看到的結果。
  • 當你發現一個嚴重的bug。
  • 當你想要添加一些新的場景或者關卡來延長遊戲的生命。
  • 以及很是多其餘的狀況...

在Cocos2d-x引擎中的如何實現熱更新

LuaEngineapache

LuaEngine是一個腳本可以實時運行Lua腳本的對象,也就是由於有了LuaEngine這個C++類對象,因此纔有了實現熱更新技術的基礎服務器

獲取LuaEngine腳本引擎的對象。網絡

    //獲取LuaEngine引擎腳本類的單例對象
    LuaEngine *engine = LuaEngine::getInstance();
    
    //設置該單例對象做爲腳本引擎管理器對象的腳本引擎
    ScriptEngineManager::getInstance()->setScriptEngine(engine);

使用LuaEngine執行Lua字符串腳本框架

    //使用Lua腳本引擎執行Lua字符串腳本
    engine->executeString("print(\"Hello 藍鷗\")");

使用LuaEngine執行Lua文件腳本函數

    //獲取儲存的文件路徑
    std::string path = FileUtils::getInstance()->getWritablePath();
    path += "hellolanou.lua";
    
    //建立一個文件指針
    //路徑、模式
    FILE* file = fopen(path.c_str(), "w");
    if (file) {
        fputs("print (\"藍鷗!!!\")", file);
        fclose(file);
    }
    else
        CCLOG("save file error.");
    
    //使用Lua腳本引擎執行Lua文件腳本
    engine->executeScriptFile(path.c_str());

 

lua_Stateui

lua_State,能夠認爲是「腳本上下文」,主要包括當前Lua腳本環境的運行狀態信息,還會有GC相關的信息。this

在使用cocos2d-x引擎開發時須要使用Lua,那麼就須要鏈接到libcocos2d和libluacocos2d兩個靜態庫。加密

也就是要在lua_State對象中註冊對應的功能模塊類,若是不想要使用裏邊相應的模塊時,就能夠在luamoduleregister.h中註釋掉對應的一行代碼。

int lua_module_register(lua_State* L)
{
    //註冊cocosdenshion模塊
    register_cocosdenshion_module(L);
    //註冊network網絡模塊
    register_network_module(L);
#if CC_USE_CCBUILDER
    //註冊cocosbuilder模塊
    register_cocosbuilder_module(L);
#endif
#if CC_USE_CCSTUDIO
    //註冊coccostudio模塊
    register_cocostudio_module(L);
#endif
    //註冊ui模塊
    register_ui_moudle(L);
    //註冊extension模塊
    register_extension_module(L);
#if CC_USE_SPINE
    //註冊spine模塊
    register_spine_module(L);
#endif
#if CC_USE_3D
    //註冊3d模塊
    register_cocos3d_module(L);
#endif
    //註冊音頻audio模塊
    register_audioengine_module(L);
    return 1;
}

在使用cocos2d-x引擎時須要使用quick框架時,一樣須要在lua_State註冊quick框架的對應模塊。

static void quick_module_register(lua_State *L)
{
    luaopen_lua_extensions_more(L);

    lua_getglobal(L, "_G");
    if (lua_istable(L, -1))//stack:...,_G,
    {
        register_all_quick_manual(L);
        // extra
        luaopen_cocos2dx_extra_luabinding(L);
        register_all_cocos2dx_extension_filter(L);
        luaopen_HelperFunc_luabinding(L);
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        luaopen_cocos2dx_extra_ios_iap_luabinding(L);
#endif
    }
    lua_pop(L, 1);
}

 

LuaStack

一般狀況下每個lua_State對象就對應一個LuaStack。

使用到的相關代碼

    //使用LuaEngine對象獲取Lua的函數棧
    LuaStack* stack = engine->getLuaStack();
#if ANYSDK_DEFINE > 0
    lua_getglobal(stack->getLuaState(), "_G");
    tolua_anysdk_open(stack->getLuaState());
    tolua_anysdk_manual_open(stack->getLuaState());
    lua_pop(stack->getLuaState(), 1);
#endif
    //設置加密用的密鑰
    stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));


AssetsManager

資源管理器的誕生就是爲了在遊戲運行時可以完成資源熱更新的技術而設計的。

這裏的資源能夠是圖片,音頻甚至是遊戲的腳本自己。

使用資源管理器,你將能夠上傳新的資源到你的服務器,你的遊戲會跟蹤遠程服務器上的修改,將新的資源下載到用戶的設備上並在遊戲中使用新的資源。

這樣的話,一個全新的設計,全新的遊戲體驗甚至全新的遊戲內容就能夠馬上被推送到用戶的受傷。

更加劇要的是,咱們並不須要針對各個渠道去從新打包咱們的應用程序而且經歷痛苦的應用審覈,這個過程當中沒有任何成本!

 

建立AssetsManager對象

    static AssetsManager *assetManager = NULL;
    
    if (!assetManager) {
        /*建立AssetsManager對象
         *@param 資源包的下載路徑
         *@param 資源包的當前版本
         *@param 資源包下載後的存儲路徑
         */
        assetManager = new AssetsManager(
                                         "http://project.lanou3g.com/game/cocos/teacher/test/src.zip",
                                         "http://project.lanou3g.com/game/cocos/teacher/test/version.php",
                                         _pathToSave.c_str());
        //設置AssetsManager對象的回調對象
        assetManager->setDelegate(this);
        //設置AssetsManager對象的timeout時間
        assetManager->setConnectionTimeout(3);
    }

 

AssetsManagerDelegateProtocol

AssetsManagerDelegateProtocal是一個類接口,主要用來封裝下載過程當中的回調接口。

class AssetsManagerDelegateProtocol
{
public:
    virtual ~AssetsManagerDelegateProtocol(){};
public:
    /* @brief Call back function for error
       @param errorCode Type of error
     * @js NA
     * @lua NA
     */
    virtual void onError(AssetsManager::ErrorCode errorCode) {};
    /** @brief Call back function for recording downloading percent
        @param percent How much percent downloaded
        @warning    This call back function just for recording downloading percent.
              AssetsManager will do some other thing after downloading, you should
              write code in onSuccess() after downloading. 
     * @js NA
     * @lua NA
     */
    virtual void onProgress(int percent) {};
    /** @brief Call back function for success
     * @js NA
     * @lua NA
     */
    virtual void onSuccess() {};
};

 

那麼接下來,咱們使用AssetsManager來建立一個自動更新的類Update.

Update.h

//
//  Update.h
//  hello
//
//  Created by 藍鷗.
//
//

#ifndef __hello__Update__
#define __hello__Update__

#include <stdio.h>
#include "cocos2d.h"
#include "extensions/cocos-ext.h"

class Update:public cocos2d::Layer,public cocos2d::extension::AssetsManagerDelegateProtocol
{
public :
    Update();
    virtual ~Update();
    
    
    virtual bool init();
    void update(cocos2d::Ref *pSender);
    void reset(cocos2d::Ref *pSender);
    
    //繼承的回調函數
    virtual void onError(cocos2d::extension::AssetsManager::ErrorCode errorCode);
    virtual void onProgress(int percent);
    virtual void onSuccess();
    CREATE_FUNC(Update);
    
private :
    cocos2d::extension::AssetsManager *getAssetsManager();
    //建立下載到的目錄路徑
    void initDownloadDir();
    
private :
    std::string _pathToSave;
    //用來顯示下載進度的Label標籤
    cocos2d::Label *_showDownloadInfo;
};

#endif /* defined(__hello__Update__) */

Update.cpp

//
//  Update.cpp
//  hello
//
//  Created by 藍鷗.
//
//

#include "Update.h"

#if(CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)

#include <dirent.h>
#include <sys/stat.h>

#endif

USING_NS_CC;
USING_NS_CC_EXT;

#define DOWNLOAD_FILE "download"

#include "CCLuaEngine.h"



Update::Update():
_pathToSave(""),
_showDownloadInfo(NULL)
{
    
}

Update::~Update()
{
    AssetsManager *assetManager = getAssetsManager();
    CC_SAFE_DELETE(assetManager);
}

bool Update::init()
{
    if (!Layer::init()) {
        return false;
    }
    
    Size winSize = Director::getInstance()->getWinSize();
    
    initDownloadDir();
    
    _showDownloadInfo = Label::createWithSystemFont("", "Arial", 20);
    _showDownloadInfo->setPosition(Vec2(winSize.width / 2,winSize.height / 2 - 20));
    this->addChild(_showDownloadInfo);
    
    auto itemLabel1 = MenuItemLabel::create(
                                            Label::createWithSystemFont("Reset", "Arail", 20), CC_CALLBACK_1(Update::reset, this));
    auto itemLabel2 = MenuItemLabel::create(
                                            Label::createWithSystemFont("Update", "Arail", 20), CC_CALLBACK_1(Update::update, this));
    
    auto menu = Menu::create(itemLabel1,itemLabel2, NULL);
    this->addChild(menu);
    
    itemLabel1->setPosition(Vec2(winSize.width / 2, winSize.height / 2 + 20));
    itemLabel2->setPosition(Vec2(winSize.width / 2, winSize.height / 2));
    
    
    menu->setPosition(Vec2::ZERO);
    
    return true;
}

void Update::onError(AssetsManager::ErrorCode code)
{
    switch (code) {
        case cocos2d::extension::AssetsManager::ErrorCode::NO_NEW_VERSION:
            _showDownloadInfo->setString("no new version");
            break;
        case cocos2d::extension::AssetsManager::ErrorCode::NETWORK:
            _showDownloadInfo->setString("no new version");
            break;
        case cocos2d::extension::AssetsManager::ErrorCode::CREATE_FILE:
            _showDownloadInfo->setString("create file error");
            break;
        default:
            break;
    }
}

void Update::onProgress(int percent)
{
    if (percent < 0) {
        return;
    }
    
    char progress[20];
    snprintf(progress, 20, "download %d%%",percent);
    
    _showDownloadInfo->setString(progress);
}

void Update::onSuccess()
{
    CCLOG("download success");
    
    _showDownloadInfo->setString("download success");
    
    std::string path = FileUtils::getInstance()->getWritablePath() + DOWNLOAD_FILE;
    
    
    LuaEngine* pEngine = LuaEngine::getInstance();
    
    //首先添加下載文件的目錄
    pEngine->addSearchPath(_pathToSave.c_str());
    
    path += "/src/main.lua";
    
    pEngine->executeScriptFile(path.c_str());
}

AssetsManager* Update::getAssetsManager()
{
    static AssetsManager *assetManager = NULL;
    
    if (!assetManager) {
        /*建立AssetsManager對象
         *@param 資源包的下載路徑
         *@param 資源包的當前版本
         *@param 資源包下載後的存儲路徑
         */
        assetManager = new AssetsManager(
                                         "http://project.lanou3g.com/game/cocos/teacher/test/src.zip",
                                         "http://project.lanou3g.com/game/cocos/teacher/test/version.php",
                                         _pathToSave.c_str());
        //設置AssetsManager對象的回調對象
        assetManager->setDelegate(this);
        //設置AssetsManager對象的timeout時間
        assetManager->setConnectionTimeout(3);
    }
    
    return assetManager;
}

void Update::initDownloadDir()
{
    CCLOG("initDownloadDir");
    
    _pathToSave = FileUtils::getInstance()->getWritablePath();
    _pathToSave += DOWNLOAD_FILE;
    
    CCLOG("Path: %s",_pathToSave.c_str());
    
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
    DIR *pDir = NULL;
    
    pDir = opendir(_pathToSave.c_str());
    
    if (!pDir) {
        mkdir(_pathToSave.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
    }
#else
    if ((GetFileAttributes(_pathToSave.c_str())) = INVALID_FILE_ATTRIBUTES) {
        CreateDirectoryA(_pathToSave.c_str(),0);
    }
#endif
    
    CCLOG("initDownloadDir end");
}

void Update::reset(Ref *pSender)
{
    _showDownloadInfo->setString("");
    
    // Remove downloaded files
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
    std::string command = "rm -r ";
    // Path may include space.
    command += "\"" + _pathToSave + "\"";
    system(command.c_str());
#else
    std::string command = "rd /s /q ";
    // Path may include space.
    command += "\"" + _pathToSave + "\"";
    system(command.c_str());
#endif
    
    getAssetsManager()->deleteVersion();
    initDownloadDir();
}

void Update::update(Ref *pSender)
{
    _showDownloadInfo->setString("");
    getAssetsManager()->update();
}

 能夠點擊下載下來網絡資源壓縮包,看下壓縮包內包含的內容。

 能夠點擊下載下來源代碼壓縮包,對應一塊兒的。

 

你們最好能夠在本地搭建一個apache服務器,作一下練習。

相關文章
相關標籤/搜索