cocos2dx资源加载机制(同步异步)

cocos2dx资源加载机制(同步异步),第1张

概述首先cocos2dx里的资源,有png,plist(pvr),exportjson(json)大致这三类,我们也从这3类去研究相应的加载代码。 本次代码分析基于: cocos2dx3.2 1、png png格式的资源,从sprite作为一个切入口来分析,一般Sprite的创建如下 Sprite* Sprite::create(const std::string& filename) 参数filen

首先cocos2dx里的资源,有png,pList(pvr),exportJson(Json)大致这三类,我们也从这3类去研究相应的加载代码。


本次代码分析基于:

cocos2dx3.2

1、png

png格式的资源,从sprite作为一个切入口来分析,一般Sprite的创建如下

Sprite* Sprite::create(const std::string& filename)

参数filename,是图片资源的路径。

内部调用的initWithfile

[cpp] view plain copy print ? Sprite*sprite=new(std::nothrow)Sprite(); if(sprite&&sprite->initWithfile(filename)) { sprite->autorelease(); returnsprite; }

initWithfile方法里

Texture2D*texture=Director::getInstance()->getTextureCache()->addImage(filename); if(texture) Rectrect=Rect::ZERO; rect.size=texture->getContentSize(); returninitWithTexture(texture,rect); }

在Texture2D * TextureCache::addImage(const std::string &path)方法是实际的载入资源的实现

//将相对路径转换成绝对路径 std::stringfullpath=fileUtils::getInstance()->fullPathForfilename(path); if(fullpath.size()==0) { returnnullptr; } //查找是否已经载入过,找到老资源,直接返回 autoit=_textures.find(fullpath); if(it!=_textures.end()) texture=it->second;

有传入的相对路径换成了绝对路径,其在找资源时,会搜索以下函数设置的搜索路径

voID fileUtils::setSearchPaths(const std::vector<std::string>& searchPaths)

boolbRet=image->initWithImagefile(fullpath); CC_BREAK_IF(!bRet); texture=newTexture2D(); if(texture&&texture->initWithImage(image)) #ifCC_ENABLE_CACHE_TEXTURE_DATA //cachethetexturefilename VolatileTextureMgr::addImageTexture(texture,fullpath); #endif //texturealreadyretained,noneedtore-retainit _textures.insert(std::make_pair(fullpath,texture));
没有找到,构造出Texture,然后按<fullpath,texture>放入_textures。以备下次下次资源载入时查找使用,

结论是:png这种资源是 资源的完全路径用来查找相应资源的。


2、pList 格式资源的载入方式

a.最原始的调用方式

voID addSpriteFramesWithfile(const std::string& pList);

b.重载方式

voID addSpriteFramesWithfile(const std::string&pList,Texture2D *texture);

voID addSpriteFramesWithfile(const std::string& pList,const std::string& texturefilename);

voID addSpriteFramesWithfile(const std::string& pList)分析如下,

//这里做了一下cached,提高效率 if(_loadedfilenames->find(pList)==_loadedfilenames->end()) //转换成全路径,同理会在搜索路径里搜索 std::stringfullPath=fileUtils::getInstance()->fullPathForfilename(pList); //解析pList,返回ValueMap ValueMapdict=fileUtils::getInstance()->getValueMapFromfile(fullPath); stringtexturePath(""); //图片资源在pList里的Metadata/texturefilename if(dict.find("Metadata")!=dict.end()) ValueMap&MetadataDict=dict["Metadata"].asValueMap(); //trytoreadtexturefilenamefromMetadata texturePath=MetadataDict["texturefilename"].asstring(); } //因为pList里的图片资源都是文件名,而pList一般是一个相对路径,拼接一下 if(!texturePath.empty()) //buildtexturepathrelativetopListfile texturePath=fileUtils::getInstance()->fullPathFromrelativefile(texturePath.c_str(),pList); else //要是pList里没有找到Metadata/texturefilename,直接就是pList去后缀,该成pList的路径+.png //buildtexturepathbyreplacingfileextension texturePath=pList; //remove.xxx size_tstartPos=texturePath.find_last_of("."); texturePath=texturePath.erase(startPos); //append.png texturePath=texturePath.append(".png"); cclOG("cocos2d:SpriteFrameCache:Tryingtousefile%sastexture",texturePath.c_str()); //熟悉的方法又来了,参考png格式资源载入的分析吧 Texture2D*texture=Director::getInstance()->getTextureCache()->addImage(texturePath.c_str()); //做一下善后的初始化工作 addSpriteFramesWithDictionary(dict,texture); <spanstyle="white-space:pre"></span>//开头怎么cached检查的,最后把自己也加入吧 _loadedfilenames->insert(pList); cclOG("cocos2d:SpriteFrameCache:Couldn'tloadtexture"); 基本分都写在代码注释里了,其实pList格式资源,图片相关资源还是最后调用的

Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(texturePath.c_str());

也是pList的图片资源,被便签为:pList的全路径改后缀为.png,但是pList里有很多子块SpriteFrame,那么这些小图块是怎么组织安排的,这些小SpriteFrame是在

voID SpriteFrameCache::addSpriteFramesWithDictionary(ValueMap& dictionary,Texture2D* texture)

中处理的,

//解析frames块 ValueMap&framesDict=dictionary["frames"].asValueMap(); intformat=0; //主要获取format数据,用来判断图块参数格式 //gettheformat if(dictionary.find("Metadata")!=dictionary.end()) ValueMap&MetadataDict=dictionary["Metadata"].asValueMap(); format=MetadataDict["format"].asInt(); //checktheformat CCASSERT(format>=0&&format<=3,"formatisnotsupportedforSpriteFrameCacheaddSpriteFramesWithDictionary:texturefilename:"); //遍历每一个frame for(autoiter=framesDict.begin();iter!=framesDict.end();++iter) ValueMap&frameDict=iter->second.asValueMap(); //pList每一个frame的key字段,其实就是这个块的原始独立文件名 std::stringspriteFramename=iter->first; SpriteFrame*spriteFrame=_spriteFrames.at(spriteFramename); if(spriteFrame) continue; ... //关键是这里,这里以每个图块的文件名作为key来索引该图块SpriteFrame, //所以经常会原点资源冲突的问题,也源于此, //虽然你的pList不冲突,但是里面冲突也不行,所以资源的命名最好定好相应规则 _spriteFrames.insert(spriteFramename,spriteFrame); SpriteFrameCache是资源冲突比较高发的地方,由于pList是很多小资源打包在一起的,所以在制作图片资源的时候,命名的规则很重要,否则就是一个坑。

3.ExportJson格式资源载入分析

ExportJson是cocostudio导出的格式,是一种Json格式,可读性的导出方式。其载入的入口是

voID ArmatureDataManager::addArmaturefileInfo(const std::string& configfilePath)

//生成一个以configfilePath为key的relativeData,在remove的时候会用得着, //相当于是一个cache,里面有armature里有的一些东西 addrelativeData(configfilePath); //资源在解析的时候就载入 _autoLoadSpritefile=true; DataReaderHelper::getInstance()->addDataFromfile(configfilePath);
一下是voID DataReaderHelper::addDataFromfile(const std::string& filePath) 的分析:

a.首先依旧是从cache机制里找一找,找到的就是已经载入过,直接放回

for(unsignedinti=0;i<_configfileList.size();i++) if(_configfileList[i]==filePath) return; _configfileList.push_back(filePath);
b.接下来,就是判断参数的后缀是.csb二进制格式,还是文本格式,打开文件的模式不一样。

//这里在读入文件时,加锁了,由于读写文件不是线程安全的,所以这里加锁,但是这个函数有在非主线程调用过吗? _dataReaderHelper->_getfileMutex.lock(); unsignedchar*pBytes=fileUtils::getInstance()->getfileData(filePath,filemode.c_str(),&filesize); std::stringcontentStr((constchar*)pBytes,filesize); _dataReaderHelper->_getfileMutex.unlock(); DataInfodataInfo; //参数的文件路径 dataInfo.filename=filePathStr; dataInfo.asyncStruct=nullptr; //参数的目录路径 dataInfo.basefilePath=basefilePath; if(str==".xml") DataReaderHelper::addDataFromCache(contentStr,&dataInfo); elseif(str==".Json"||str==".ExportJson") //本次只分析该载入方式 DataReaderHelper::addDataFromJsonCache(contentStr,&dataInfo); elseif(isbinaryfilesrc) DataReaderHelper::addDataFromBinaryCache(contentStr.c_str(),43); Font-size:13.63636302948px; Font-family:Arial; line-height:26px"> 在voID DataReaderHelper::addDataFromJsonCache(const std::string& fileContent,DataInfo *dataInfo)中,开始解析ExportJson里的东西。过滤utf bom,解析Json

紧接着是几板斧,

1)解析armatures

2)解析animations

3)解析textures

我们关注图片资源的载入方式,前2种在此略过。

//ExportJson文件中texture_data字段下纹理个数 length=DICTOol->getArrayCount_Json(Json,TEXTURE_DATA); for(inti=0;i<length;i++) constrAPIdJson::Value&textureDic=DICTOol->getSubDictionary_Json(Json,TEXTURE_DATA,i); //解析texture_data,看看下面关于texture_data的格式示例 TextureData*textureData=decodeTexture(textureDic); //在同步加载方式时,这里为空,后面分析异步在分析 if(dataInfo->asyncStruct) _dataReaderHelper->_addDataMutex.lock(); //载入当前这个texture_data的图片资源 //这样的第一个参数是图块的名称,第三个参数为exportJson的路径 ArmatureDataManager::getInstance()->addTextureData(textureData->name.c_str(),textureData,dataInfo->filename.c_str()); //textureData创建时1,addTextureData是加入Map结构retain了一次,变成了2,这里release一下,变成1. textureData->release(); if(dataInfo->asyncStruct) _dataReaderHelper->_addDataMutex.unlock();


关于texture_data的Json格式有哪些内容:

[plain] { "name":"png/shitouren01_R_xiabi", "wIDth":83.0, "height":88.0,226)"> "pX":0.0,226)"> "pY":1.0,226)"> "pListfile":"" },
基本对应着类TextureData

voID ArmatureDataManager::addTextureData(const std::string& ID,TextureData *textureData,const std::string& configfilePath)

//还记得最开始的时候,就为本exportJson创建了一个relativeData,226)"> if(relativeData*data=getrelativeData(configfilePath)) //纹理资源放入对应的容器里,这里放入的子块的名称 data->textures.push_back(ID); //对字块名称与其对应的texturedata建立一种映射,方便查找 _textureDatas.insert(ID,textureData);
最后解析的最后,开始解析资源配置字段了,

//根据前面的分析,ArmatureDataManager::getInstance()->isautoLoadSpritefile()返回为true boolautoLoad=dataInfo->asyncStruct==nullptr?ArmatureDataManager::getInstance()->isautoLoadSpritefile():dataInfo->asyncStruct->autoLoadSpritefile; if(autoLoad) //分析config_file_path字段 //Json[CONfig_file_PATH].IsNull()?0:Json[CONfig_file_PATH].Size(); constchar*path=DICTOol->getStringValueFromArray_Json(Json,CONfig_file_PATH,i);//Json[CONfig_file_PATH][i].IsNull()?nullptr:Json[CONfig_file_PATH][i].GetString(); if(path==nullptr) cclOG("loadCONfig_file_PATHerror."); std::stringfilePath=path; filePath=filePath.erase(filePath.find_last_of(".")); //异步加载方式 dataInfo->configfileQueue.push(filePath); else//同步加载 //这里直接写死了,一个png,一个pList, //实际在exportJson导出的格式,是有config_png_path与config_file_path std::stringpListPath=filePath+".pList"; std::stringpngPath=filePath+".png"; //这里开始加入图片资源了 ArmatureDataManager::getInstance()->addSpriteFrameFromfile((dataInfo->basefilePath+pListPath).c_str(),(dataInfo->basefilePath+pngPath).c_str(),43); Font-size:13.63636302948px; Font-family:Arial; line-height:26px"> exprotJson里资源配置示例如下:

[HTML] "config_file_path":[ "020.pList" ],226)"> "config_png_path":[ "020.png" ]
资源载入方法voID ArmatureDataManager::addSpriteFrameFromfile(const std::string& pListPath,const std::string& imagePath,const std::string& configfilePath)里

//将pList信息保存至relativeData data->pListfiles.push_back(pListPath); //SpriteFrameCacheHelper只是SpriteFrameCache的简单包装,实际就是调用的SpriteFrameCache::addSpriteFrameFromfile //pListPath是exportJson的路径改后缀为pList,同理imagePath SpriteFrameCacheHelper::getInstance()->addSpriteFrameFromfile(pListPath,imagePath);
至此,armature资源载入流程分析完毕,总结下armature:

在texturedata中,是子块的名称为key的,我们通过分析SpriteFrameCache知道,其内部资源也是以字块为key的,在cocostudio里我们设计动作或者ui的时候,都是子块的名称,

综合来分析:

单个png资源,是以该资源的全路径为key的,由TextureCache来维持

pList资源集式的资源,其依赖的png,依然是上述方式,不过在其基础上,通过SpriteFrameCache做了一层二级的缓存机制,是以里面每个子块名称作为key映射相关rect信息的SpriteFrame,

异步载入分析:

从了解的情况来看,有cocos2dx提供2种资源异步加载方式,一个原始图片资源的异步加载

voID TextureCache::addImageAsync(const std::string &path,const std::function<voID(Texture2D*)>& callback)

另一个就是上面我们接触到的Armature的异步加载方式,

voID ArmatureDataManager::addArmaturefileInfoAsync(const std::string& configfilePath,Ref *target,SEL_SCHEDulE selector)

下面我逐一分析,先从原始图片资源异步加载方式开刀:

<spanstyle="white-space:pre"></span>Texture2D*texture=nullptr; //将路径转换成全路径 //先从cache查找一下,有直接返回 autoit=_textures.find(fullpath); if(it!=_textures.end()) texture=it->second; if(texture!=nullptr) //找到了,调用一下回调方法 callback(texture); //异步加载需要用到的一些结构 //lazyinit if(_asyncStructQueue==nullptr) _asyncStructQueue=newqueue<AsyncStruct*>(); _imageInfoQueue=newdeque<ImageInfo*>(); //createanewthreadtoloadimages //开辟新的线程来处理本次加载任务,主要是防止重复加载,并实际加载图片资源,加载完之后放入_imageInfoqueue队列, //等待TextureCache::addImageAsyncCallBack来处理 _loadingThread=newstd::thread(&TextureCache::loadImage,this); _needQuit=false; if(0==_asyncRefCount) //每帧调用,主要处理<span>_imageInfoQueue,构造Texture2D,</span> Director::getInstance()->getScheduler()->schedule(schedule_selector(TextureCache::addImageAsyncCallBack),this,false); ++_asyncRefCount; //开始构造异步加载的一些数据结构,给加载线程TextureCache::loadImage使用 //放入的是资源的全路径以及加载完成时的回调 //这个数据结构是new出来的,在TextureCache::addImageAsyncCallBack里释放 //generateasyncstruct AsyncStruct*data=newAsyncStruct(fullpath,callback); //这里产生了任务,等待工作线程来处理 //addasyncstructintoqueue _asyncStructQueueMutex.lock(); _asyncStructQueue->push(data); _asyncStructQueueMutex.unlock(); //告诉一下工作线程,有任务了 _sleepCondition.notify_one();
结合上述的注释,基本上可以理解图片资源异步加载的基本原理了。

下面是Armature的异步加载分析:

voIDArmatureDataManager::addArmaturefileInfoAsync(conststd::string&configfilePath,Ref*target,SEL_SCHEDulEselector) //同同步加载,建立一个以configfilePath为key的relativeData,用于remove addrelativeData(configfilePath); // _autoLoadSpritefile=true; // DataReaderHelper::getInstance()->addDataFromfileAsync("","",configfilePath,target,selector); if(target&&selector) if(_asyncRefTotalCount==0&&_asyncRefCount==0) (target->*selector)(1); (target->*selector)((_asyncRefTotalCount-_asyncRefCount)/(float)_asyncRefTotalCount); return; 一开始,依旧是从cache里查找一下,看是不是已经加载了,加载了,则调用下回调函数,这里的统计任务个数,后面会讲到,将本加载配置文件加入cache中。

//准备异步加载需要的数据结构 //lazyinit if(_asyncStructQueue==nullptr) _asyncStructQueue=newstd::queue<AsyncStruct*>(); _dataQueue=newstd::queue<DataInfo*>(); //开辟工作线程,用来解析exportJson //基本同图片资源异步加载方式,只不过这里调用的只是解析,DataReaderHelper::addDataFromJsonCache //完成解析后,构造DataInfo数据,交给DataReaderHelper::addDataAsyncCallBack来处理 _loadingThread=newstd::thread(&DataReaderHelper::loadData,this); need_quit=false; if(0==_asyncRefCount) //用来加载DataInfo中的configQueue,还记得DataReaderHelper::addDataFromJsonCache里异步加载部分吧,就是在哪里push进去的 //最后调用ArmatureDataManager::addSpriteFrameFromfile来加载pList,png资源,你没看错,所以Armature异步加载是不完整的 Director::getInstance()->getScheduler()->schedule(schedule_selector(DataReaderHelper::addDataAsyncCallBack),0); background-color:inherit">//回调时告诉回调函数的进度 ++_asyncRefCount; ++_asyncRefTotalCount; //由于回调是成员方法,方式其宿主提前释放 if(target) target->retain(); @H_403_2041@ voIDDataReaderHelper::addDataAsyncCallBack(floatdt) //取任务 DataInfo*pDataInfo=dataQueue->front(); dataQueue->pop(); _dataInfoMutex.unlock(); AsyncStruct*pAsyncStruct=pDataInfo->asyncStruct; //当调用voIDArmatureDataManager::addArmaturefileInfoAsync( //conststd::string&imagePath,conststd::string&pListPath,conststd::string&configfilePath,... //时调用 if(pAsyncStruct->imagePath!=""&&pAsyncStruct->pListPath!="") _getfileMutex.lock(); ArmatureDataManager::getInstance()->addSpriteFrameFromfile(pAsyncStruct->pListPath.c_str(),pAsyncStruct->imagePath.c_str(),pDataInfo->filename.c_str()); _getfileMutex.unlock(); //这个就是在DataReaderHelper::addDataFromJsonCache产生的 while(!pDataInfo->configfileQueue.empty()) std::stringconfigPath=pDataInfo->configfileQueue.front(); _getfileMutex.lock(); //这里是正规的加载SpriteFrame,所以你的先自己吧pList资源加载进来,通过cache来加速 ArmatureDataManager::getInstance()->addSpriteFrameFromfile((pAsyncStruct->basefilePath+configPath+".pList").c_str(),(pAsyncStruct->basefilePath+configPath+".png").c_str(),pDataInfo->filename.c_str()); _getfileMutex.unlock(); pDataInfo->configfileQueue.pop(); Ref*target=pAsyncStruct->target; SEL_SCHEDulEselector=pAsyncStruct->selector; //本次任务结束 --_asyncRefCount; //调用回调 if(target&&selector) //回调参数完成百分比 //还记得之前retain过吧 target->release(); //销毁辅助结构 deletepAsyncStruct; deletepDataInfo; //没有任务,就取消每帧调用 _asyncRefTotalCount=0; Director::getInstance()->getScheduler()->unschedule(schedule_selector(DataReaderHelper::addDataAsyncCallBack),this);
从上面的分析,我们可以看出Armature的异步加载,只是部分,而不是全部,只是把解析部分交给了线程,图片资源还是需要自己通过图片资源异步加载方式加载。


原文地址:http://www.jb51.cc/article/p-hmnrzdoc-bkc.html 总结

以上是内存溢出为你收集整理的cocos2dx资源加载机制(同步/异步)全部内容,希望文章能够帮你解决cocos2dx资源加载机制(同步/异步)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1050119.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-25
下一篇2022-05-25

发表评论

登录后才能评论

评论列表(0条)

    保存