Cocos2dx 异步加载纹理

前端之家收集整理的这篇文章主要介绍了Cocos2dx 异步加载纹理前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1 异步加载资源

  异步加载的执行过程主要在addImageAsync函数中: 判定当前路径对应的纹理是否已经加载,如果未加载,为该纹理创建一个AsyncStruct 数据结构存放该纹理数据,并加入至_requestMutex请求队列中。在创建的用于专门解析纹理的新线程LoadImage中,请求队列中的数据将被逐个解析至AsyncStruct .image中并加入已解析队列_responseQueue中。 在创建的计时器回调函数addImageAsyncCallBack中,将解析得到的image转换为最终的texture。
  上述过程涉及两个线程:主线程GL Thread与子线程Load Thread,过程中两个重要的数据为AsyncStruct 与Image Data。其中AsyncStruct的创建与析构都在主线程中完成,Image Data在Load Thread中创建,在GL Thread中析构。
  

  1. void TextureCache::addImageAsync(const std::string &path,const std::function<void(Texture2D*)>& callback,const std::string& callbackKey)
  2. {
  3. Texture2D *texture = nullptr;
  4. // 确定图片纹理是否已经加载
  5. std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path);
  6. auto it = _textures.find(fullpath);
  7. if (it != _textures.end())
  8. texture = it->second;
  9.  
  10. // 已加载 直接执行回调函数
  11. if (texture != nullptr)
  12. {
  13. if (callback) callback(texture);
  14. return;
  15. }
  16.  
  17. // 检查当前路径对应的文件是否存在
  18. if (fullpath.empty() || !FileUtils::getInstance()->isFileExist(fullpath)) {
  19. if (callback) callback(nullptr);
  20. return;
  21. }
  22.  
  23. // lazy init
  24. if (_loadingThread == nullptr)
  25. {
  26. // 创建一个新的线程 用于加载纹理
  27. _loadingThread = new (std::nothrow) std::thread(&TextureCache::loadImage,this);
  28. _needQuit = false;//线程是否终止的标志
  29. }
  30.  
  31. if (0 == _asyncRefCount)
  32. {
  33. // 创建定时器回调 用于将解析的image转为texture并执行回调函数
  34. Director::getInstance()->getScheduler()->schedule(CC_SCHEDULE_SELECTOR(TextureCache::addImageAsyncCallBack),this,0,false);
  35. }
  36. ++_asyncRefCount;
  37.  
  38. // 生成async struct,并加入至请求队列
  39. AsyncStruct *data =
  40. new (std::nothrow) AsyncStruct(fullpath,callback,callbackKey);
  41.  
  42. // add async struct into queue
  43. _asyncStructQueue.push_back(data); // 该队列在unbind函数中使用 在加载纹理过程中不使用
  44. _requestMutex.lock();
  45. _requestQueue.push_back(data);
  46. _requestMutex.unlock();
  47.  
  48. _sleepCondition.notify_one();
  49. }
  50.  
  51. // 解析资源的线程
  52. void TextureCache::loadImage()
  53. {
  54. AsyncStruct *asyncStruct = nullptr;
  55. std::mutex signalMutex;
  56. std::unique_lock<std::mutex> signal(signalMutex);
  57. while (!_needQuit)
  58. {
  59. // 从请求队列中取出待解析资源
  60. _requestMutex.lock();
  61. if (_requestQueue.empty())
  62. {
  63. asyncStruct = nullptr;
  64. }
  65. else
  66. {
  67. asyncStruct = _requestQueue.front();
  68. _requestQueue.pop_front();
  69. }
  70. _requestMutex.unlock();
  71.  
  72. if (nullptr == asyncStruct) {
  73. _sleepCondition.wait(signal);
  74. continue;
  75. }
  76.  
  77. // load image
  78. asyncStruct->loadSuccess = asyncStruct->image.initWithImageFileThreadSafe(asyncStruct->filename);
  79.  
  80. // push the asyncStruct to response queue
  81. _responseMutex.lock();
  82. _responseQueue.push_back(asyncStruct);
  83. _responseMutex.unlock();
  84. }
  85. }
  86.  
  87. void TextureCache::addImageAsyncCallBack(float /*dt*/)
  88. {
  89. Texture2D *texture = nullptr;
  90. AsyncStruct *asyncStruct = nullptr;
  91. while (true) // 轮询解析队列中的image
  92. {
  93. // pop an AsyncStruct from response queue
  94. _responseMutex.lock();
  95. if (_responseQueue.empty())
  96. {
  97. asyncStruct = nullptr;
  98. }
  99. else
  100. {
  101. asyncStruct = _responseQueue.front();
  102. _responseQueue.pop_front();
  103.  
  104. // 保持_asyncStructQueue 与 _responseQueue的同步
  105. CC_ASSERT(asyncStruct == _asyncStructQueue.front());
  106. _asyncStructQueue.pop_front();
  107. }
  108. _responseMutex.unlock();
  109.  
  110. if (nullptr == asyncStruct) {
  111. break;
  112. }
  113.  
  114. // check the image has been convert to texture or not
  115. auto it = _textures.find(asyncStruct->filename);
  116. if (it != _textures.end()) // 检查当前纹理资源是否已被加载
  117. {
  118. texture = it->second;
  119. }
  120. else
  121. {
  122. // convert image to texture
  123. if (asyncStruct->loadSuccess)
  124. {
  125. Image* image = &(asyncStruct->image);
  126. // generate texture in render thread
  127. texture = new (std::nothrow) Texture2D();
  128.  
  129. texture->initWithImage(image,asyncStruct->pixelFormat);
  130. //parse 9-patch info
  131. this->parseNinePatchImage(image,texture,asyncStruct->filename);
  132. #if CC_ENABLE_CACHE_TEXTURE_DATA
  133. // cache the texture file name
  134. VolatileTextureMgr::addImageTexture(texture,asyncStruct->filename);
  135. #endif
  136. // cache the texture. retain it,since it is added in the map
  137. _textures.emplace(asyncStruct->filename,texture);
  138. texture->retain(); // 为何要retain: 下一帧不自动释放该纹理,纹理的释放由程序手动控制
  139. texture->autorelease();
  140. }
  141. else {
  142. texture = nullptr;
  143. CCLOG("cocos2d: Failed to call TextureCache::addImageAsync(%s)",asyncStruct->filename.c_str());
  144. }
  145. }
  146.  
  147. // 执行回调函数
  148. if (asyncStruct->callback)
  149. {
  150. (asyncStruct->callback)(texture);
  151. }
  152.  
  153. // release the asyncStruct
  154. delete asyncStruct;
  155. --_asyncRefCount;
  156. }
  157.  
  158. if (0 == _asyncRefCount) // 所有异步加载过程均已完成 销毁计时器回调
  159. {
  160. Director::getInstance()->getScheduler()->unschedule(CC_SCHEDULE_SELECTOR(TextureCache::addImageAsyncCallBack),this);
  161. }
  162. }

2 纹理中关键帧的检索

检索之前,首先需要将一整张纹理按照区域切割成各个关键帧,并建立帧名与帧之间的map映射表。addSpriteFramesWithFile完成了这一过程,该函数为已加载完成的纹理数据与对应的plist文件构建映射关系。

  1. void SpriteFrameCache::addSpriteFramesWithFile(const std::string& plist,Texture2D *texture)
  2. {
  3. if (_loadedFileNames->find(plist) != _loadedFileNames->end())
  4. {
  5. return; // We already added it
  6. }
  7.  
  8. std::string fullPath = FileUtils::getInstance()->fullPathForFilename(plist);// plist全路径
  9. ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(fullPath); // plist是XML文件,cocos将其解析为key-value的map结构
  10.  
  11. addSpriteFramesWithDictionary(dict,texture);
  12. _loadedFileNames->insert(plist);
  13. }
  14.  
  15. // 从texture中取出相应区域spriteframe
  16. void SpriteFrameCache::addSpriteFramesWithDictionary(ValueMap& dictionary,Texture2D* texture)
  17. {
  18. /* Supported Zwoptex Formats: ZWTCoordinatesFormatOptionXMLLegacy = 0,// Flash Version ZWTCoordinatesFormatOptionXML1_0 = 1,// Desktop Version 0.0 - 0.4b ZWTCoordinatesFormatOptionXML1_1 = 2,// Desktop Version 1.0.0 - 1.0.1 ZWTCoordinatesFormatOptionXML1_2 = 3,// Desktop Version 1.0.2+ */
  19.  
  20. if (dictionary["frames"].getType() != cocos2d::Value::Type::MAP)
  21. return;
  22.  
  23. ValueMap& framesDict = dictionary["frames"].asValueMap(); // 存放每一帧图片路径、大小、偏移、对应的纹理区域位置等属性
  24. int format = 0;
  25.  
  26. Size textureSize;
  27.  
  28. // get the format
  29. if (dictionary.find("Metadata") != dictionary.end())
  30. {
  31. ValueMap& MetadataDict = dictionary["Metadata"].asValueMap(); // Metadata中存放纹理格式、纹理大小、纹理名称等纹理属性
  32. format = MetadataDict["format"].asInt();
  33. if(MetadataDict.find("size") != MetadataDict.end())
  34. {
  35. textureSize = SizeFromString(MetadataDict["size"].asString());
  36. }
  37. }
  38.  
  39. // check the format
  40. CCASSERT(format >=0 && format <= 3,"format is not supported for SpriteFrameCache addSpriteFramesWithDictionary:textureFilename:");
  41.  
  42. auto textureFileName = Director::getInstance()->getTextureCache()->getTextureFilePath(texture);
  43. Image* image = nullptr;
  44. NinePatchImageParser parser;
  45. for (auto& iter : framesDict)
  46. {
  47. ValueMap& frameDict = iter.second.asValueMap();
  48. std::string spriteFrameName = iter.first;
  49. SpriteFrame* spriteFrame = _spriteFrames.at(spriteFrameName);
  50. if (spriteFrame)
  51. {
  52. continue; // 当前帧已加入至关键帧集合 无需处理
  53. }
  54.  
  55. if(format == 0)
  56. {
  57. float x = frameDict["x"].asFloat();
  58. float y = frameDict["y"].asFloat();
  59. float w = frameDict["width"].asFloat();
  60. float h = frameDict["height"].asFloat();
  61. float ox = frameDict["offsetX"].asFloat();
  62. float oy = frameDict["offsetY"].asFloat();
  63. int ow = frameDict["originalWidth"].asInt();
  64. int oh = frameDict["originalHeight"].asInt();
  65. // check ow/oh
  66. if(!ow || !oh)
  67. {
  68. CCLOGWARN("cocos2d: WARNING: originalWidth/Height not found on the SpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist");
  69. }
  70. // abs ow/oh
  71. ow = std::abs(ow);
  72. oh = std::abs(oh);
  73. // create frame
  74. spriteFrame = SpriteFrame::createWithTexture(texture,Rect(x,y,w,h),false,Vec2(ox,oy),Size((float)ow,(float)oh)
  75. );
  76. }
  77. else if(format == 1 || format == 2)
  78. {
  79. Rect frame = RectFromString(frameDict["frame"].asString());
  80. bool rotated = false;
  81.  
  82. // rotation
  83. if (format == 2)
  84. {
  85. rotated = frameDict["rotated"].asBool();
  86. }
  87.  
  88. Vec2 offset = PointFromString(frameDict["offset"].asString());
  89. Size sourceSize = SizeFromString(frameDict["sourceSize"].asString());
  90.  
  91. // create frame
  92. spriteFrame = SpriteFrame::createWithTexture(texture,frame,rotated,offset,sourceSize
  93. );
  94. }
  95. else if (format == 3)
  96. {
  97. // get values
  98. Size spriteSize = SizeFromString(frameDict["spriteSize"].asString());
  99. Vec2 spriteOffset = PointFromString(frameDict["spriteOffset"].asString());
  100. Size spriteSourceSize = SizeFromString(frameDict["spriteSourceSize"].asString());
  101. Rect textureRect = RectFromString(frameDict["textureRect"].asString());
  102. bool textureRotated = frameDict["textureRotated"].asBool();
  103.  
  104. // get aliases
  105. ValueVector& aliases = frameDict["aliases"].asValueVector();
  106.  
  107. for(const auto &value : aliases) {
  108. std::string oneAlias = value.asString();
  109. if (_spriteFramesAliases.find(oneAlias) != _spriteFramesAliases.end())
  110. {
  111. CCLOGWARN("cocos2d: WARNING: an alias with name %s already exists",oneAlias.c_str());
  112. }
  113.  
  114. _spriteFramesAliases[oneAlias] = Value(spriteFrameName);
  115. }
  116.  
  117. // create frame
  118. spriteFrame = SpriteFrame::createWithTexture(texture,Rect(textureRect.origin.x,textureRect.origin.y,spriteSize.width,spriteSize.height),textureRotated,spriteOffset,spriteSourceSize);
  119.  
  120. if(frameDict.find("vertices") != frameDict.end())
  121. {
  122. std::vector<int> vertices;
  123. parseIntegerList(frameDict["vertices"].asString(),vertices);
  124. std::vector<int> verticesUV;
  125. parseIntegerList(frameDict["verticesUV"].asString(),verticesUV);
  126. std::vector<int> indices;
  127. parseIntegerList(frameDict["triangles"].asString(),indices);
  128.  
  129. PolygonInfo info;
  130. initializePolygonInfo(textureSize,spriteSourceSize,vertices,verticesUV,indices,info);
  131. spriteFrame->setPolygonInfo(info);
  132. }
  133. if (frameDict.find("anchor") != frameDict.end())
  134. {
  135. spriteFrame->setAnchorPoint(PointFromString(frameDict["anchor"].asString()));
  136. }
  137. }
  138.  
  139. bool flag = NinePatchImageParser::isNinePatchImage(spriteFrameName);
  140. if(flag)
  141. {
  142. if (image == nullptr) {
  143. image = new (std::nothrow) Image();
  144. image->initWithImageFile(textureFileName);
  145. }
  146. parser.setSpriteFrameInfo(image,spriteFrame->getRectInPixels(),spriteFrame->isRotated());
  147. texture->addSpriteFrameCapInset(spriteFrame,parser.parseCapInset());
  148. }
  149. // add sprite frame
  150. _spriteFrames.insert(spriteFrameName,spriteFrame);
  151. }
  152. CC_SAFE_DELETE(image);
  153. }
  154.  
  155. // 基于frameName检索得到SpriteFrame
  156. SpriteFrame* SpriteFrameCache::getSpriteFrameByName(const std::string& name)
  157. {
  158. SpriteFrame* frame = _spriteFrames.at(name);
  159. if (!frame)
  160. {
  161. // try alias dictionary
  162. if (_spriteFramesAliases.find(name) != _spriteFramesAliases.end())
  163. {
  164. std::string key = _spriteFramesAliases[name].asString();
  165. if (!key.empty())
  166. {
  167. frame = _spriteFrames.at(key);
  168. if (!frame)
  169. {
  170. CCLOG("cocos2d: SpriteFrameCache: Frame aliases '%s' isn't found",key.c_str());
  171. }
  172. }
  173. }
  174. else
  175. {
  176. CCLOG("cocos2d: SpriteFrameCache: Frame '%s' isn't found",name.c_str());
  177. }
  178. }
  179. return frame;
  180. }

3 lua层的接口调用

  1. local createAni = function ( texture )
  2. -- 回调函数时将传入已加载的纹理
  3. local testSp = cc.Sprite:createWithTexture(texture)
  4. self:addChild(testSp)
  5.  
  6. local ani = cc.Animation:create()
  7. ani:setDelayPerUnit(0.12)
  8. ani:setRestoreOriginalFrame(true) -- 动画停止时显示为起始图片
  9. for i=1,num do
  10. local frameName = getFrameName(i)
  11. local frame = cc.SpriteFrameCache:getInstance():getSpriteFrameByName(frameName)
  12. if frame then
  13. ani:addSpriteFrame(frame)
  14. end
  15. -- 关键帧全部加载完毕 播放动画
  16. if i == num then
  17. self:stopAllActions()
  18. local action = cc.Animation:create(ani)
  19. self:runAction(action)
  20. end
  21. end
  22. end
  23. local textureName = getTextureName() or "" -- 禁止cocos接口中传入nil 避免宕机
  24. cc.Director:getInstance():getTextureCache():addImageAsync(textureName,createAni)

猜你在找的Cocos2d-x相关文章