精通Cocos2d-x游戏开发(进阶卷)
上QQ阅读APP看书,第一时间看更新

3.1 使用AssetsManagerEx

AssetsManagerEx的使用非常简单,需要用到两个类,AssetsManagerEx和EventListenerAssetsManagerEx, AssetsManagerEx用来执行增量更新,EventListenerAssetsManagerEx用来监听增量更新中触发的各种事件,如各种更新失败的原因、进度更新以及更新成功等事件。使用的示例代码如下。

        //传入Manifest路径和缓存路径,创建AssetsManagerEx对象
        auto AssetsManager = AssetsManagerEx::create(manifestPath, storagePath);
        AssetsManager->retain();
        //传入AssetsManagerEx对象和回调函数,EventListenerAssetsManagerEx可以捕获
        AssetsManagerEx触发的各种事件
        auto listener = EventListenerAssetsManagerEx::create(AssetsManager, callback);
        //监听事件需要先将EventListenerAssetsManagerEx添加到EventDispatcher
        Director::getInstance()->getEventDispatcher()->addEventListenerWithFixe
        dPriority(listener , 1);
        //最后执行AssetsManagerEx的update()方法自动更新
        AssetsManager->update();

上面的代码简单描述了使用AssetsManagerEx的步骤,接下来详细介绍一下AssetsManagerEx的使用。首先是AssetsManagerEx,增量更新的核心功能都由AssetsManagerEx实现,需要传入Manifest文件的路径和相对于WritablePath可写路径的缓存路径来初始化AssetsManagerEx。Manifest文件是用于检查版本更新的文件,在后面会详细介绍,缓存路径指的是增量更新文件下载后存储的路径,由于权限等问题,从服务器下载的资源并不能替换程序安装目录下的原始资源,所以我们会下载到一个可写的缓存目录下,在加载资源的时候优先加载缓存目录中的资源,如找不到则再去加载安装目录中的资源。AssetsManagerEx的常用接口如下。

        //检查是否有更新
        void checkUpdate();
        //自动检查是否有更新,有则自动更新
        void update();
        //下载更新失败的部分资源
        void downloadFailedAssets();
        //获取本地Manifest对象
        const Manifest* getLocalManifest() const;

update()方法和checkUpdate()方法的区别是,update()方法会检查更新并自动执行更新,而checkUpdate()方法则只是检查是否有更新,如果希望实现一些非强制性的更新,或者在更新之前弹出一个对话框,由玩家来决定是否更新等,就可以使用checkUpdate()方法。

checkUpdate()方法的返回值是void,那么我们如何知道是否需要更新呢?通过消息!我们需要创建一个EventListenerAssetsManagerEx对象来监听AssetsManagerEx触发的消息,当检查到新版本时,AssetsManagerEx会触发NEW_VERSION_FOUND消息,而不需要更新时,则会触发ALREADY_UP_TO_DATE消息,AssetsManagerEx会触发的所有消息如下。

        enum class EventCode
        {
            ERROR_NO_LOCAL_MANIFEST,    //本地Manifest错误
            ERROR_DOWNLOAD_MANIFEST,    //下载Manifest失败
            ERROR_PARSE_MANIFEST,       //解析Manifest失败
            NEW_VERSION_FOUND,          //检查到新版本
            ALREADY_UP_TO_DATE,         //已经是最新版本
            UPDATE_PROGRESSION,         //更新进度刷新消息
            ASSET_UPDATED,              //有资源下载成功
            ERROR_UPDATING,             //有文件下载失败
            UPDATE_FINISHED,            //更新完成
            UPDATE_FAILED,              //更新失败
            ERROR_DECOMPRESS            //解压文件失败
        };

可调用EventListenerAssetsManagerEx的create()方法传入AssetsManagerEx对象和回调函数,创建对象,然后将EventListenerAssetsManagerEx对象添加到EventDispatcher中,当消息触发时会调用回调函数,将事件对象传入到回调函数中。示例教程中的TutorialUpdateAssets示例演示了如何使用AssetsManagerEx进行热更新,示例代码如下。

        std::string storage = FileUtils::getInstance()->getWritablePath() +
        "test1/";
        auto assetMgrEx = AssetsManagerEx::create("project.manifest", storage);
        assetMgrEx->retain();

        auto amListener = cocos2d::extension::EventListenerAssetsManagerEx::
        create(assetMgrEx, [this](EventAssetsManagerEx* event){
            switch (event->getEventCode())
            {
            case EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST:
            {
              //本地Manifest文件错误
              CCLOG("No local manifest file found, skip assets update.");
              this->onLoadEnd();
            }
              break;

            case EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION:
            {
              //更新进度
              std::string assetId = event->getAssetId();
              float percent = event->getPercent();
              std::string str;
              if (assetId == AssetsManagerEx::VERSION_ID)
              {
                  str = StringUtils::format("Version file: %.2f", percent) + "%";
              }
              else if (assetId == AssetsManagerEx::MANIFEST_ID)
              {
                  str = StringUtils::format("Manifest file: %.2f", percent) + "%";
              }
              else
              {
                  str = StringUtils::format("%.2f", percent) + "%";
              }
              CCLOG("asset %s download %s Percent", assetId.c_str(), str.c_str());
            }
              break;
            case EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST:
            case EventAssetsManagerEx::EventCode::ERROR_PARSE_MANIFEST:
            {
              //下载或解析Manifest文件失败
              CCLOG("Fail to download manifest file, update skipped.");
              this->onLoadEnd();
            }
              break;
            case EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE:
            case EventAssetsManagerEx::EventCode::UPDATE_FINISHED:
            {
              //最新版本不需要更新或更新完成
              CCLOG("Update finished. %s", event->getMessage().c_str());
              this->onLoadEnd();
            }
              break;
            case EventAssetsManagerEx::EventCode::UPDATE_FAILED:
            {
              //更新失败
              CCLOG("Update failed. %s", event->getMessage().c_str());
              event->getAssetsManagerEx()->downloadFailedAssets();
              this->onLoadEnd();
            }
              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;
        );
        //自动检查是否有更新,有则自动更新
        void update();
        //下载更新失败的部分资源
        void downloadFailedAssets();
        //获取本地Manifest对象
        const Manifest* getLocalManifest() const;
          }
        });
        //将EventListenerAssetsManagerEx添加到EventDispatcher中
        Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority
        (amListener, 1);
        assetMgrEx->update();

在回调函数中可以获取EventAssetsManagerEx对象,从该对象中获取消息ID、CURL错误码、错误消息、资源ID、AssetsManagerEx、总的下载进度以及当前文件的下载进度等信息。

需要注意的是创建AssetsManagerEx时传入的Manifest文件和下载路径,我们传入的project.manifest文件是安装包中的一个Manifest文件,它记录了本地的资源列表以及服务器的Manifest文件地址,后面会详细介绍Manifest文件。下载路径需要是一个可写的路径,所以需要获取WritablePath作为前缀,注意不要和其他路径混合在一起,这个路径只用来存放增量更新更新下来的文件,不要将游戏的存档等信息放到这个路径下。

示例不论更新成功还是失败,最终都会调用onLoadEnd()方法,在onLoadEnd()方法中创建一个Sprite,如果更新失败会根据本地的图片创建Sprite,如果更新成功则会根据更新下来的图片创建Sprite。

        void TutorialUpdateAssets::onLoadEnd()
        {
            auto backgroundSprite = Sprite::create("Images/background1.jpg");
            addChild(backgroundSprite, 1);
            backgroundSprite->setPosition(Director::getInstance()->getWinSize()*0.5f);
        }