Cocos2d-x has an extension that manages resources. It is called AssetsManagerExtension
. This extension is designed for a hot update of resources such as images and audio files. You can update a new version of resources on your games by using this extension without updating your applications.
Before using AssetsManagerExtension
, you should learn about it. This extension has many useful features to help you make the hot update. Some of these features are as follows:
You have to prepare a web server, and hence, your application will download resources.
You need to upload resources and manifest files. In this case, we will update HelloWorld.png
and a .zip
file called test.zip
. This .zip
file includes some new images. AssetsManagerExtension
will download resources according to the manifest files. The manifest files are version.manifest
and project.manifest
.
The version.manifest
file contains the following code:
{ "packageUrl" : "http://example.com/assets_manager/", "remoteVersionUrl" : "http://example.com/assets_manager/version.manifest", "remoteManifestUrl" : "http://example.com/assets_manager/project.manifest", "version" : "1.0.1",}
The project.manifest
file contains the following code:
{ "packageUrl" : "http://example.com/assets_manager/", "remoteVersionUrl" : "http://example.com/assets_manager/version.manifest", "remoteManifestUrl" : "http://example.com/assets_manager/project.manifest", "version" : "1.0.1", "assets" : { "HelloWorld.png" : { "md5" : "b7892dc221c840550847eaffa1c0b0aa" }, "test.zip" : { "md5" : "c7615739e7a9bcd1b66e0018aff07517", "compressed" : true } } }
Then, you have to upload these manifest files and new resources.
Next, you have to prepare your application for a hot update. You have to create the local.manifest
file in your project. The local manifest file should contain the following code:
{ "packageUrl" : "http://example.com/assets_manager/", "remoteVersionUrl" : "http://example.com/assets_manager/version.manifest", "remoteManifestUrl" : "http://example.com/assets_manager/project.manifest", "version" : "1.0.0", }
You should make a class that manages AssetsManagerExtension
in your project. Here, we create a class called ResourceManager
. Firstly, you will create a header file of ResourceManager
. It is called ResourceManager.h
. This file contains the following code:
#include "cocos2d.h" #include "extensions/cocos-ext.h" class ResourceManager { private: ResourceManager(); static ResourceManager* instance; cocos2d::extension::AssetsManagerEx* _am; cocos2d::extension::EventListenerAssetsManagerEx* _amListener; public: // custom event name static const char* EVENT_PROGRESS; static const char* EVENT_FINISHED; virtual ~ResourceManager(); static ResourceManager* getInstance(); void updateAssets(std::string manifestPath); };
The next step is to create a ResourceManager.cpp
file. This file contains the following code:
#include "ResourceManager.h" USING_NS_CC; USING_NS_CC_EXT; // custom event name const char* ResourceManager::EVENT_PROGRESS = "__cc_Resource_Event_Progress"; const char* ResourceManager::EVENT_FINISHED = "__cc_Resource_Event_Finished"; ResourceManager* ResourceManager::instance = nullptr; ResourceManager::~ResourceManager() { CC_SAFE_RELEASE_NULL(_am); } ResourceManager::ResourceManager() :_am(nullptr) ,_amListener(nullptr) { } ResourceManager* ResourceManager::getInstance() { if (instance==nullptr) { instance = new ResourceManager(); } return instance; } void ResourceManager::updateAssets(std::string manifestPath) { std::string storagePath = FileUtils::getInstance()- >getWritablePath(); CCLOG("storage path = %s", storagePath.c_str()); if (_am!=nullptr) { CC_SAFE_RELEASE_NULL(_am); } _am = AssetsManagerEx::create(manifestPath, storagePath); _am->retain(); if (!_am->getLocalManifest()->isLoaded()) { CCLOG("Fail to update assets, step skipped."); } else { _amListener = EventListenerAssetsManagerEx::create(_am, [this](EventAssetsManagerEx* event){ static int failCount = 0; switch (event->getEventCode()) { case EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST: { CCLOG("No local manifest file found, skip assets update."); break; } case EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION: { std::string assetId = event->getAssetId(); float percent = event->getPercent(); std::string str; if (assetId == AssetsManagerEx::VERSION_ID) { // progress for version file } else if (assetId == AssetsManagerEx::MANIFEST_ID) { // progress for manifest file } else { // dispatch progress event CCLOG("%.2f Percent", percent); auto event = EventCustom(ResourceManager::EVENT_PROGRESS); auto data = Value(percent); event.setUserData(&data); Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); } break; } case EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST: case EventAssetsManagerEx::EventCode::ERROR_PARSE_MANIFEST: { CCLOG("Fail to download manifest file, update skipped."); break; } case EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE: case EventAssetsManagerEx::EventCode::UPDATE_FINISHED: { CCLOG("Update finished. %s", event->getMessage().c_str()); CC_SAFE_RELEASE_NULL(_am); // dispatch finished updating event Director::getInstance()->getEventDispatcher()- >dispatchCustomEvent(ResourceManager::EVENT_FINISHED); break; } case EventAssetsManagerEx::EventCode::UPDATE_FAILED: { CCLOG("Update failed. %s", event- >getMessage().c_str()); // retry 5 times, if error occurred failCount ++; if (failCount < 5) { _am->downloadFailedAssets(); } else { CCLOG("Reach maximum fail count, exit update process"); failCount = 0; } break; } case EventAssetsManagerEx::EventCode::ERROR_UPDATING: { CCLOG("Asset %s : %s", event- >getAssetId().c_str(), event->getMessage().c_str()); break; } case EventAssetsManagerEx::EventCode::ERROR_DECOMPRESS: { CCLOG("%s", event->getMessage().c_str()); break; } default: break; } }); // execute updating resources Director::getInstance()->getEventDispatcher()- >addEventListenerWithFixedPriority(_amListener, 1); _am->update(); } }
Finally, to start updating the resource, use the following code:
// label for progress auto size = Director::getInstance()->getWinSize(); TTFConfig config("fonts/arial.ttf", 30); _progress = Label::createWithTTF(config, "0%", TextHAlignment::CENTER); _progress->setPosition( Vec2(size.width/2, 50) ); this->addChild(_progress); // progress event getEventDispatcher()- >addCustomEventListener(ResourceManager::EVENT_PROGRESS, [this](EventCustom* event){ auto data = (Value*)event->getUserData(); float percent = data->asFloat(); std::string str = StringUtils::format("%.2f", percent) + "%"; CCLOG("%.2f Percent", percent); if (this->_progress != nullptr) { this->_progress->setString(str); } }); // fnished updating event getEventDispatcher()- >addCustomEventListener(ResourceManager::EVENT_FINISHED, [this](EventCustom* event){ // clear cache Director::getInstance()->getTextureCache()- >removeAllTextures(); // reload scene auto scene = HelloWorld::createScene(); Director::getInstance()->replaceScene(scene); }); // update resources ResourceManager::getInstance()- >updateAssets("res/local.manifest");
Firstly, we will explain the manifest file and the mechanism of AssetsManagerExtension
. The manifest files are in the JSON format. Local manifest and version manifest have the following data:
Keys |
Description |
---|---|
|
The URL where the assets manager will try to request and download all the assets. |
|
The remote version manifest file URL that permits one to check the remote version to determine whether a new version has been uploaded to the server. |
|
The remote manifest file URL that contains all the asset information. |
|
The version of this manifest file. |
In addition, the remote manifest has the following data in the key called assets.
Keys |
Description |
---|---|
|
Each key represents the relative path of the asset. |
|
The |
|
When the compressed field is |
AssetsManagerExtension
will execute the hot update in the following steps:
AssetsManagerExtension
downloads the project manifest according to the remote manifest URL in the local manifest.md5
value in the remote manifest to the md5
of the asset in the application.md5
values do not match, AssetsManagerExtension
downloads this asset.AssetsManagerExtension
will use the version manifest that was downloaded instead of the local manifest.Next, we will explain the ResourceManager
class. You can execute the hot update as follows:
ResourceManager::getInstance()->updateAssets("res/local.manifest");
You should call the ResourceManager::updateAssets
method by specifying the path of the local manifest. ResourceManager::updateAssets
will create an instance of AssetsManagerEx
, which is the class name of AssetsManagerExtension
, by specifying the path of the local manifest and the path of the storage in the application.
It will create an instance of EventListenerAssetsManagerEx
for listening to the progress of the hot update.
If the compressed value is true, AssetsManagerExtension
will unzip it after downloading it.
You can update assets by calling the AssetsManagerEx::update
method. During the update, you can get the following events:
Event |
Description |
---|---|
|
Cannot find the local manifest. |
|
Get the progression of the update. |
|
Fail to download the manifest file. |
|
Parse error for the manifest file. |
|
Already updating assets (The version in the local manifest and the version in the version manifest are equal.). |
|
Finished updating assets. |
|
Error occurred during updating assets. In this case, the cause of error may be the connection. You should try to update again. |
|
Failed to update. |
|
Error occurred during unzipping. |
ResourceManager
dispatches the event called EVENT_PROGRESS
if it catches the event called UPDATE_PROGRESSION
. If you catch EVENT_PROGRESS
, you should update the progress label.
Further, it dispatches the event called EVENT_FINISHED
if it catches the event called UPDATE_FINISHED
. If you catch EVENT_FINISHED
, you should refresh all textures. That's why we remove all texture caches and reload the scene.
// clear cache Director::getInstance()->getTextureCache()->removeAllTextures(); // reload sceneauto scene = HelloWorld::createScene(); Director::getInstance()->replaceScene(scene);
18.218.173.117