官术网_书友最值得收藏!

6.2 異步加載CSB

即使使用了緩存的方案,首次加載CSB文件還是會阻塞一段時間,因為這里面還包含了紋理的加載,如果要加載的紋理比較大或者要加載多個紋理,或者要同時加載多個CSB文件,那么就會有比較明顯的卡頓。如果能夠將CSB文件進行異步加載,就可以很好地改善這個問題。CSLoader是不支持異步加載CSB的,如果將CSB文件的加載分為加載紋理和創建節點兩部分,那么創建節點這部分是無法做到線程安全的!因為各種節點在創建時操作了各種單例對象,如從TextureCache中獲取紋理,在EventDispatcher中注冊觸摸事件等。在子線程和主線程中同時操作這些資源,很可能導致程序崩潰或出現其他異常。

使用了緩存方案之后,主要的瓶頸在紋理加載這里,所以可以使用TextureCache的異步加載紋理的方法,將CSB所需的紋理進行異步加載,加載完之后再在主線程中執行創建節點的邏輯。接下來的問題就是如何知道每個CSB需要加載哪些紋理。可以通過一個簡單的方法解析CSB文件,得到所需的紋理,但這個方法的效率不高,所以最好是通過另外一個簡單的程序,生成一個配置表,在配置表中記錄每個CSB文件所需的紋理列表,然后直接使用這個配置表。使用下面的方法可以遞歸找出一個CSB文件加載所需的全部紋理。

        //傳入CSB文件的Data,以及用于保存紋理文件名的set,查找單個CSB所引用的所有紋理
        void CCsbLoader::searchTexturesByCsbFile(Data& data, set<string>& texSet)
        {
            auto csparsebinary = GetCSParseBinary(data.getBytes());
            auto textures = csparsebinary->textures();
            int textureSize = csparsebinary->textures()->size();
            for (int i = 0; i < textureSize; ++i)
            {
              string plistFile = FileUtils::getInstance()->fullPathForFilename
                (textures->Get(i)->c_str());
              if (m_LoadingPlists.find(plistFile) ! = m_LoadingPlists.end()
                  || SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded
                  (plistFile))
              {
                  continue;
              }
              m_LoadingPlists.insert(plistFile);
              Data plistData = FileUtils::getInstance()->getDataFromFile
              (plistFile);
              if (plistData.isNull())
              {
                  continue;
              }

              string textureFile;
              ValueMap dict = FileUtils::getInstance()->getValueMapFromData(
                  reinterpret_cast<const char*>(plistData.getBytes()), plistData.
                  getSize());

              if (dict.find("metadata") ! = dict.end())
              {
                  ValueMap& metadataDict = dict["metadata"].asValueMap();
                  textureFile = metadataDict["textureFileName"].asString();
              }

              if (! textureFile.empty())
              {
                  //計算相對路徑,將紋理的文件名對應到plist的路徑下
                  textureFile = FileUtils::getInstance()->fullPathFromRelativeFile
                  (textureFile, plistFile);
              }
              else
              {
                  //如果plist文件中沒有紋理路徑名,則嘗試讀取plist對應的.png
                  textureFile = plistFile;
                  //將xxxx.plist結尾的.plist移除,替換成.png
                  textureFile = textureFile.erase(textureFile.find_last_of("."));
                  textureFile = textureFile.append(".png");
              }

              //該紋理未被加載且沒有在待加載列表中,則添加進texSet中
              if (Director::getInstance()->getTextureCache()->getTextureForKey
              (textureFile) == nullptr
                  && m_LoadingTextures.find(textureFile) == m_ LoadingTextures.end())

              {
                  m_LoadingTextures.insert(textureFile);
                  texSet.insert(textureFile);
              }
            }
        }

searchTexturesByCsbNodeTree()方法可以遞歸查找一個CSB節點的所有嵌套CSB文件所引用到的紋理,傳入一個對象和一個set容器,CSB文件所引用到的紋理都會被存儲到容器中。

      void CCsbLoader::searchTexturesByCsbNodeTree(const flatbuffers::NodeTree*
      tree, set<string>& texSet)
      {
          //對所有的子節點做相同的處理
          auto children = tree->children();
          int size = children->size();
          for (int i = 0; i < size; ++i)
          {
            auto subNodeTree = children->Get(i);
            //對于CsbNode子節點,需要一并加載進來
            auto options = subNodeTree->options();
            std::string classname = subNodeTree->classname()->c_str();
            if (classname == "ProjectNode")
            {
                auto projectNodeOptions = (ProjectNodeOptions*)options->data();
                std::string filePath = FileUtils::getInstance()->
                fullPathForFilename(
                    projectNodeOptions->fileName()->c_str());

                //有此文件且未加載過該文件
                //如果已經搜索過,則沒必要再搜索
                if (! filePath.empty()
                    && m_CsbNodes.find(filePath) == m_CsbNodes.end()
                    && m_CheckedCsb.find(filePath) == m_CheckedCsb.end())
                {
                    m_CheckedCsb.insert(filePath);
                    Data data = FileUtils::getInstance()->getDataFromFile
                    (filePath);
                    if (! data.isNull())
                    {
                        m_CsbFileCache[filePath] = data;
                        //找到這個CSB所引用的Png
                        searchTexturesByCsbFile(data, texSet);
                        auto csparsebinary = GetCSParseBinary(data.getBytes());
                        //對該CSB進行遞歸
                        searchTexturesByCsbNodeTree(csparsebinary->nodeTree(),
                        texSet);
                    }
                }
            }
            else
            {
                searchTexturesByCsbNodeTree(subNodeTree, texSet);
            }
          }
      }
主站蜘蛛池模板: 若尔盖县| 德令哈市| 贵溪市| 高密市| 北碚区| 大悟县| 沈丘县| 洮南市| 白山市| 嘉义县| 三台县| 肇源县| 鹤庆县| 建始县| 绿春县| 临泉县| 万山特区| 胶州市| 利川市| 宜宾县| 泽州县| 泰顺县| 马关县| 永清县| 星子县| 江津市| 正安县| 景东| 铅山县| 遂川县| 墨玉县| 二连浩特市| 札达县| 木里| 武川县| 高平市| 麦盖提县| 宣化县| 芮城县| 乐山市| 柘荣县|