天天看點

【COCOS2DX-LUA 腳本開發之十二】利用AssetsManager實作線上更新資源檔案

首先說明一個問題:

為什麼要線上更新資源和腳本檔案!?

對于此問題,那要說的太多了,簡單概括,如果你的項目已經在google play 或Apple Store 等平台上架了,那麼當你項目需要做一些活動或者修改前端的一些代碼等那麼你需要重新送出一個新版本給平台,這時候你的上架時候是個不确定的時候,具體什麼時候能上架,主要跟平台有關,你再着急,也沒有用的。

那麼如果你的項目是使用腳本語言進行編寫的,例如lua,js等等,那麼一旦你有需要更新你的項目,你完全可以通過從伺服器下載下傳最新的腳本和資源來實作線上更新,免去很多煩惱,至少更新再也不需要平台的稽核來限制了不是麼~(有些平台是禁止線上更新資源方式的,但是你懂得)

那麼如何在項目中實作線上更新呢?則是本章具體需要跟大家分享的教程啦。

下面進入本章的重要内容:

在cocos2dx 2.x 引擎的擴充包(extensions)中有一個 AssetsManager

AssetsManager 主要功能就是下載下傳資源到本地,并幫你解壓!

如果大家還不知道這個類,那麼可以先到cocos2dx引擎的http:///Users/slater/Documents/cocos2d-2.1rc0-x-2.1.2-hotfix/samples/Cpp/AssetsManagerTest 目錄下運作示例。

 (注:目前Himi使用的是cocos2dx-2.1.2hotfix版本這個示例在我的mac os無法正常運作)

下面Himi建立個項目來詳細講解AssetsManager:

Himi這裡拿lua項目進行,首先建立一個新的cocos2dx-lua 的項目:

第一步:将項目中Resoures目錄下的 hello.lua 删除!

     第二步:在AppDelegate.h 中添加如下代碼:

先導入所需的頭檔案:

#include "cocos2d.h" 

#include "AssetsManager.h" 

#include "cocos-ext.h" 

using namespace std; 

using namespace cocos2d; 

using namespace extension; 

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) 

#include <dirent.h> 

#include <sys/stat.h> 

#endif 

繼續添加變量和方法名:

void updateFiles(); 

void createDownDir(); 

string pathToSave; 

pathToSave 變量用于儲存下載下傳的路徑!用于添加到  CCLuaEngine 引擎中,這樣便于CCLuaEngine查找Lua檔案!

 第三步:在AppDelegate.cpp 中添加如下代碼:

static AssetsManager* pAssetsManager; 

void AppDelegate::updateFiles(){ 

    createDownDir(); 

    pAssetsManager = new AssetsManager("https://raw.github.com/HimiGame/himigame/master/hello.zip", "https://raw.github.com/HimiGame/himigame/master/version"); 

    if(pAssetsManager->checkUpdate()){ 

        if( pAssetsManager->update() ){//改源碼 

            CCLuaEngine* pEngine = CCLuaEngine::defaultEngine(); 

            CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine); 

            //首先添加下載下傳檔案的目錄 

            pEngine->addSearchPath(pathToSave.c_str()); 

            //繼續添加本地hello2的路徑到CCLuaEngine中 

            string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello2.lua"); 

            pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str()); 

            //運作下載下傳檔案hello.lua 

            string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua"); 

            pEngine->executeScriptFile(runLua.c_str()); 

        } 

    } 

void AppDelegate::createDownDir(){ 

    pathToSave = CCFileUtils::sharedFileUtils()->getWritablePath(); 

    pathToSave += "Himi"; 

    // Create the folder if it doesn't exist 

    DIR *pDir = NULL; 

    pDir = opendir (pathToSave.c_str()); 

    if (! pDir) 

    { 

        mkdir(pathToSave.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); 

#else 

    if ((GetFileAttributesA(pathToSave.c_str())) == INVALID_FILE_ATTRIBUTES) 

        CreateDirectoryA(pathToSave.c_str(), 0); 

首先介紹createDwomDir函數:

(注:所有連接配接都是Hmi在GitHub伺服器中的,大家可以是以通路)!

此函數主要用于在項目目錄下建立一個檔案夾,到底建立到哪裡,你不用管,交給如下函數:

CCFileUtils::sharedFileUtils()->getWritablePath();

上面這個函數能從ios、android平台自動找到可寫入的路徑!

createDwomDir 函數中  pathToSave += “Himi”;  主要作用是在getWritablePath()路徑後自定義一個目錄名!需要不需要都可以的,如果想建立個,那就自定義即可,名字無所謂思密達。

繼續介紹  updateFiles 函數:

此函數中,首先我們調用 createDwomDir 函數用于建立我們新的寫入目錄,并且将目錄儲存到pathToSave變量中。

然後我們建立了一個 AssetsManager 執行個體,這裡要靜态。AssetsManager建立函數有兩種,如下:

1. AssetsManager::AssetsManager(const char* packageUrl, const char* versionFileUrl) 

2. AssetsManager::AssetsManager(const char* packageUrl, const char* versionFileUrl, const char* storagePath) 

首先看第一種建立函數:

參數1 :  packgeUrl: 表示需要下載下傳更新的zip包的url位址

參數2 : versionFileUrl :表示擷取目前伺服器版本号的rul,用于比對用戶端是否需要更新!

第二種建立方式多了一個參數: storagePath 表示我們的自定義包名,如createDwomDir函數中的pathToSave += “Himi” 一句功能一樣。

而在AssetsManager類中封裝了很多方法,例如檢查是否需要更新、更新下載下傳檔案、擷取packageUrl等。具體方法可看AssetsManager源碼!

pAssetsManager->checkUpdate() :通過得到伺服器傳回的版本号與本地版本号進行比對如不一緻則傳回true,反之傳回false

一旦通過判斷checkUpdate函數傳回true,我們即可調用AssetsManager中的update進行檔案更新!

這裡要注意:由于目前AssetsManager的源碼中并沒有給予我們判斷檔案下載下傳成功的函數!是以Himi與AssetsManager作者聯系,我們可以更改update函數讓其傳回bool類型即可!

修改方式如下:首先我們到源碼AssetsManager.h中将如下upate函數修改:

virtual void update(); 

修改成如下: 

virtual bool update(); 

繼續到 AssetsManager.cpp 中update函數進行修改成如下:

修改為: 

bool AssetsManager::update() 

    // 1. Urls of package and version should be valid; 

    // 2. Package should be a zip file. 

    if (_versionFileUrl.size() == 0 || 

        _packageUrl.size() == 0 || 

        std::string::npos == _packageUrl.find(".zip")) 

        CCLOG("no version file url, or no package url, or the package is not a zip file"); 

        return false; 

    // Check if there is a new version. 

    if (! checkUpdate()) return false; 

    // Is package already downloaded? 

    string downloadedVersion = CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_DOWNLOADED_VERSION); 

    if (downloadedVersion != _version) 

        if (! downLoad()) return false; 

        // Record downloaded version. 

        CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_DOWNLOADED_VERSION, _version.c_str()); 

        CCUserDefault::sharedUserDefault()->flush(); 

    // Uncompress zip file. 

    if (! uncompress()) return false; 

    // Record new version code. 

    CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_VERSION, _version.c_str()); 

    // Unrecord downloaded version code. 

    CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_DOWNLOADED_VERSION, ""); 

    CCUserDefault::sharedUserDefault()->flush(); 

    // Set resource search path. 

    setSearchPath(); 

    // Delete unloaded zip file. 

    string zipfileName = _storagePath + TEMP_PACKAGE_FILE_NAME; 

    if (remove(zipfileName.c_str()) != 0) 

        CCLOG("can not remove downloaded zip file"); 

    return true; 

當我們做了如此的修改後,那麼當檔案下載下傳完成後則會傳回true!

最後我們來看如下代碼:

CCLuaEngine* pEngine = CCLuaEngine::defaultEngine(); 

CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine); 

//首先添加下載下傳檔案的目錄 

pEngine->addSearchPath(pathToSave.c_str()); 

//繼續添加本地hello2的路徑到CCLuaEngine中 

string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello2.lua"); 

pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str()); 

//運作下載下傳檔案hello.lua 

string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua"); 

pEngine->executeScriptFile(runLua.c_str()); 

首先我們将檔案更新下來的路徑通過setScriptEngine添加到 CCLuaEngine中,然後将hello2.lua 的路徑也添加到CCLuaEngine的搜尋途徑中,這樣一來 CCLuaEngine 會從我們設定的這兩個路徑中去找我們在lua中require的對應lua檔案!這一步設定必須設定!因為CCLuaEngine不像cocos2dx那樣自動幫我們找檔案路徑!CCLuaEngine 是不存在路徑的,是以我們要手動設定CCLuaEngine搜尋路徑,以便找到對應的lua檔案!

也正是因為CCLuaEngine不會自動幫我們找檔案路徑,是以我們運作lua腳本時,必須将運作的腳本lua檔案完整的路徑傳入,如下:

      下面我們開始書寫測試代碼:

在AppDelegate.cpp中的  applicationDidFinishLaunching 函數中注釋一些代碼并且添加測試代碼,修改後的 applicationDidFinishLaunching 函數内容如下:

bool AppDelegate::applicationDidFinishLaunching() 

    // initialize director 

    CCDirector *pDirector = CCDirector::sharedDirector(); 

    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView()); 

    // turn on display FPS 

    pDirector->setDisplayStats(true); 

    // set FPS. the default value is 1.0/60 if you don't call this 

    pDirector->setAnimationInterval(1.0 / 60); 

    // register lua engine 

//    CCLuaEngine* pEngine = CCLuaEngine::defaultEngine(); 

//    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()->fullPathForFilename("hello.lua"); 

//    pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str()); 

//    pEngine->executeScriptFile(path.c_str()); 

//#endif 

    //删除hello.lua 

    pathToSave=""; 

    updateFiles(); 

下面開始運作!需要注意的是我們本地是完全不存在hello.lua檔案的,是以一旦我們運作成功出現畫面說明已經利用AssetsManager成功的線上下載下傳了hello.lua檔案!

運作截圖如下:

<a href="http://www.himigame.com/wp-content/uploads/2013/04/QQ20130419-1.png"></a>

從如上的運作截圖中可以看出,首先我們得到伺服器傳來的版本号2.1.1,然後進行checkUpdate函數,此函數是從本地的存儲檔案找是否有版本号,如果沒有那麼就預設為可下載下傳,如果有則會對比,如不一緻則進行更新。

那麼當檔案完整下載下傳下來之後update函數則自動會我們在本地儲存最新從伺服器拿到的版本号!緊接着update函數還為我們進行了對zip檔案的解壓,解壓成功後會自動删除zip包!

是以如果大家運作過自己的這個項目成功下載下傳運作了,那麼下載下傳運作請删除項目後再運作,因為第一次的成功運作已經将最新版本号記錄儲存下來了,你也可以通過修改伺服器版本号或者删除項目的存儲檔案。

總結本文的教程:

第一:     CCLuaEngine 引擎是不會自動幫我們找檔案的,是以你一旦有一個新的運作腳本的路徑,一定要通過  CCLuaEngine的 addSearchPath函數告知!這樣的話,當你的一個lua檔案采用require其他腳本檔案,CCLuaEngine就會在你 addSearchPath的路徑中進行查找!

第二: 如果你想讓自己項目自帶的腳本與下載下傳腳本同時使用,例如自己項目有a.lua 其中a.lua 中包含一句代碼: requireb  ”b”   ,而b.lua是你通過線上更新下載下傳下來的。那麼a.lua 和 b.lua的路徑都要通過 addSearchPath 設定下各自的路徑。

第三: lua engine應該也是支援搜尋路徑的優先級的,是以你可以通過控制pEngine-&gt;addSearchPath()的調用順序,進而控制當你本地項目與下載下傳更新同時擁有同一個名字的腳本等資源,可以優先選擇使用哪個!

第四: 在AppStore 規定不允許在主遊戲線程中進行聯網,然後我們使用的AssetsManager的下載下傳更新卻是在聯網下載下傳,是以大家要使用異步來做!另外及時沒有這條規定我想童鞋們也不會讓聯網放主遊戲線程中吧!

第五:AssetsManager 中還有其他的功能,更多的功能請大家自己看cocos2dx引擎的示例項目!

最後給出Himi的示例項目下載下傳位址:

    本篇就到這裡,希望對各位沒有做過、正要做此功能的童鞋們提供幫助!

本文轉自 xiaominghimi 51CTO部落格,原文連結:http://blog.51cto.com/xiaominghimi/1181590,如需轉載請自行聯系原作者

繼續閱讀