Managing resource files

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.

Getting ready

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:

  • Multithread downloading support
  • Two-level progression support—File-level and byte-level progression
  • Compressed ZIP file support
  • Resuming download
  • Detailed progression information and error information
  • Possibility to retry failed assets

You have to prepare a web server, and hence, your application will download resources.

How to do it...

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");

How it works...

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

packageUrl

The URL where the assets manager will try to request and download all the assets.

remoteVersionUrl

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.

remoteManifestUrl

The remote manifest file URL that contains all the asset information.

version

The version of this manifest file.

In addition, the remote manifest has the following data in the key called assets.

Keys

Description

key

Each key represents the relative path of the asset.

Md5

The md5 field represents the version information of the asset.

compressed

When the compressed field is true, the downloaded file will be decompressed automatically; this key is optional.

AssetsManagerExtension will execute the hot update in the following steps:

  1. Read the local manifest in the application.
  2. Download the version manifest according to the remote version URL in the local manifest.
  3. Compare the version in the local manifest to the version in the version manifest.
  4. If both versions do not match, AssetsManagerExtension downloads the project manifest according to the remote manifest URL in the local manifest.
  5. Compare the md5 value in the remote manifest to the md5 of the asset in the application.
  6. If both md5 values do not match, AssetsManagerExtension downloads this asset.
  7. Next time, 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

ERROR_NO_LOCAL_MANIFEST

Cannot find the local manifest.

UPDATE_PROGRESSION

Get the progression of the update.

ERROR_DOWNLOAD_MANIFEST

Fail to download the manifest file.

ERROR_PARSE_MANIFEST

Parse error for the manifest file.

ALREADY_UP_TO_DATE

Already updating assets (The version in the local manifest and the version in the version manifest are equal.).

UPDATE_FINISHED

Finished updating assets.

UPDATE_FAILED

Error occurred during updating assets. In this case, the cause of error may be the connection. You should try to update again.

ERROR_UPDATING

Failed to update.

ERROR_DECOMPRESS

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);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.173.117