轉自:http://www.himigame.com/iphone-cocos2dx/1354.htmlhtml
首先說明一個問題:前端
爲何要在線更新資源和腳本文件!?android
對於此問題,那要說的太多了,簡單歸納,若是你的項目已經在google play 或Apple Store 等平臺上架了,那麼當你項目須要作一些活動或者修改前端的一些代碼等那麼你須要從新提交一個新版本給平臺,這時候你的上架時候是個不肯定的時候,具體何時能上架,主要跟平臺有關,你再着急,也沒有用的。ios
那麼若是你的項目是使用腳本語言進行編寫的,例如lua,js等等,那麼一旦你有須要更新你的項目,你徹底能夠經過從服務器下載最新的腳本和資源來實如今線更新,免去不少煩惱,至少更新不再須要平臺的審覈來限制了不是麼~(有些平臺是禁止在線更新資源方式的,可是你懂得)git
那麼如何在項目中實如今線更新呢?則是本章具體須要跟你們分享的教程啦。github
(有童鞋問我,單機怎麼辦? 通常本身搭個服務器,專用於在線更新。不過通常單機不這麼作,這套下載更新主要用於網遊 )服務器
下面進入本章的重要內容:app
在cocos2dx 2.x 引擎的擴展包(extensions)中有一個 AssetsManagercurl
AssetsManager 主要功能就是下載資源到本地,並幫你解壓!iphone
若是你們還不知道這個類,那麼能夠先到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 #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 ((GetFileAttributesA(pathToSave.c_str())) == INVALID_FILE_ATTRIBUTES) { CreateDirectoryA(pathToSave.c_str(), 0); } #endif }
首先介紹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類型便可!
(注:update 函數中對版本、下載文件、解壓、存儲最新版本號等作了判斷,所以當此函數返回true,則完成一切操做)
修改方式以下:首先咱們到源碼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文件完整的路徑傳入,以下:
//運行下載文件hello.lua string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua"); pEngine->executeScriptFile(runLua.c_str());
下面咱們開始書寫測試代碼:
在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(); return true; }
下面開始運行!須要注意的是咱們本地是徹底不存在hello.lua文件的,因此一旦咱們運行成功出現畫面說明已經利用AssetsManager成功的在線下載了hello.lua文件!
運行截圖以下:
從如上的運行截圖中能夠看出,首先咱們獲得服務器傳來的版本號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->addSearchPath()的調用順序,從而控制當你本地項目與下載更新同時擁有同一個名字的腳本等資源,能夠優先選擇使用哪一個!
第四: 在AppStore 規定不容許在主遊戲線程中進行聯網,而後咱們使用的AssetsManager的下載更新倒是在聯網下載,因此你們要使用異步來作!另外及時沒有這條規定我想童鞋們也不會讓聯網放主遊戲線程中吧!
第五:AssetsManager 中還有其餘的功能,更多的功能請你們本身看cocos2dx引擎的示例項目!
附:http://blog.csdn.net/sonikk/article/details/8871403
源碼相關文件路徑:
C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions\cocos-ext.h
C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions\AssetsManager\AssetsManager.h
C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions\AssetsManager\AssetsManager.cpp
引用方法:
#include "cocos-ext.h"
#include "AssetsManager.h"
using namespace cocos2d::extension;
Additional Include Directories:
C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions
C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions\AssetsManager
Additional Library Directories:
C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\Debug.win32
Additinal Dependencies:
libcurl_imp.lib
libExtensions.lib
該AssetsManager的基本流程:
1. 配置須要更新的zip的URL,更新版本號的URL,更新存放的相對路徑
2. 從Server獲取該zip文件的版本號
3. 對比Client中的UserDefault.xml中current-version-code鍵的值(當前版本號)是否過時
4. 若Server的版本比Client的新,則經過http請求下載該zip
5. 解壓縮該zip文件
6. 下載後經過CCFileUtils的fullPathForFilename方法來獲取文件的引用
下載流程:
update():
1. 配置的zip的URL和version的URL必須合法,且非空
2. 檢驗Server是否存在新版本
3. 讀取UserDefault.xml的downloaded-version-code,對比當前版本號,若不相等則進行zip包下載
4. 若下載完成,記錄最新的版本號於UserDefault.xml的downloaded-version-code中,並flush刷新
5. 解壓縮zip包
6. 若解壓成功,記錄最新的版本號於UserDefault.xml的current-version-code中,並把downloaded-version-code刪除,並flush刷新
7. 設置搜索路徑,(先獲取搜索路徑vector,而後將新的搜索路徑插入到該vector中,將該vector從新放入CCFileUtils中)
8. 刪除未加載的cocos2dx-update-temp-package.zip文件