本文共 15176 字,大约阅读时间需要 50 分钟。
首先cocos2dx里的资源,有png,plist(pvr),exportjson(json)大致这三类,我们也从这3类去研究相应的加载代码。
本次代码分析基于:
cocos2dx3.2
1、png
png格式的资源,从sprite作为一个切入口来分析,一般Sprite的创建如下
Sprite* Sprite::create(const std::string& filename)
参数filename,是图片资源的路径。
内部调用的initWithFile
- Sprite *sprite = new (std::nothrow) Sprite();
- if (sprite && sprite->initWithFile(filename))
- {
- sprite->autorelease();
- return sprite;
- }
在initWithFile方法里
- Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
- if (texture)
- {
- Rect rect = Rect::ZERO;
- rect.size = texture->getContentSize();
- return initWithTexture(texture, rect);
- }
在Texture2D * TextureCache::addImage(const std::string &path)方法是实际的载入资源的实现
-
- std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path);
- if (fullpath.size() == 0)
- {
- return nullptr;
- }
-
- auto it = _textures.find(fullpath);
- if( it != _textures.end() )
- texture = it->second;
有传入的相对路径换成了绝对路径,其在找资源时,会搜索以下函数设置的搜索路径
void FileUtils::setSearchPaths(const std::vector<std::string>& searchPaths)
- bool bRet = image->initWithImageFile(fullpath);
- CC_BREAK_IF(!bRet);
-
- texture = new Texture2D();
-
- if( texture && texture->initWithImage(image) )
- {
- #if CC_ENABLE_CACHE_TEXTURE_DATA
-
- VolatileTextureMgr::addImageTexture(texture, fullpath);
- #endif
-
- _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)分析如下,
-
- if (_loadedFileNames->find(plist) == _loadedFileNames->end())
- {
-
- std::string fullPath = FileUtils::getInstance()->fullPathForFilename(plist);
-
- ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(fullPath);
-
- string texturePath("");
-
-
- if (dict.find("metadata") != dict.end())
- {
- ValueMap& metadataDict = dict["metadata"].asValueMap();
-
- texturePath = metadataDict["textureFileName"].asString();
- }
-
-
- if (!texturePath.empty())
- {
-
- texturePath = FileUtils::getInstance()->fullPathFromRelativeFile(texturePath.c_str(), plist);
- }
- else
- {
-
-
- texturePath = plist;
-
-
- size_t startPos = texturePath.find_last_of(".");
- texturePath = texturePath.erase(startPos);
-
-
- texturePath = texturePath.append(".png");
-
- CCLOG("cocos2d: SpriteFrameCache: Trying to use file %s as texture", texturePath.c_str());
- }
-
-
- Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(texturePath.c_str());
-
- if (texture)
- {
-
- addSpriteFramesWithDictionary(dict, texture);
- <span style="white-space:pre"> </span>
- _loadedFileNames->insert(plist);
- }
- else
- {
- CCLOG("cocos2d: SpriteFrameCache: Couldn't load texture");
- }
- }
基本分都写在代码注释里了,其实plist格式资源,图片相关资源还是最后调用的
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(texturePath.c_str());
也是plist的图片资源,被便签为:plist的全路径改后缀为.png,但是plist里有很多子块SpriteFrame,那么这些小图块是怎么组织安排的,这些小SpriteFrame是在
void SpriteFrameCache::addSpriteFramesWithDictionary(ValueMap& dictionary, Texture2D* texture)
中处理的,
-
- ValueMap& framesDict = dictionary["frames"].asValueMap();
- int format = 0;
-
-
-
- if (dictionary.find("metadata") != dictionary.end())
- {
- ValueMap& metadataDict = dictionary["metadata"].asValueMap();
- format = metadataDict["format"].asInt();
- }
-
-
- CCASSERT(format >=0 && format <= 3, "format is not supported for SpriteFrameCache addSpriteFramesWithDictionary:textureFilename:");
-
-
- for (auto iter = framesDict.begin(); iter != framesDict.end(); ++iter)
- {
- ValueMap& frameDict = iter->second.asValueMap();
-
- std::string spriteFrameName = iter->first;
- SpriteFrame* spriteFrame = _spriteFrames.at(spriteFrameName);
- if (spriteFrame)
- {
- continue;
- }
- ...
-
-
-
-
- _spriteFrames.insert(spriteFrameName, spriteFrame);
- }
SpriteFrameCache是资源冲突比较高发的地方,由于plist是很多小资源打包在一起的,所以在制作图片资源的时候,命名的规则很重要,否则就是一个坑。
3. ExportJson格式资源载入分析
ExportJson是cocostudio导出的格式,是一种json格式,可读性的导出方式。其载入的入口是
void ArmatureDataManager::addArmatureFileInfo(const std::string& configFilePath)
-
-
- addRelativeData(configFilePath);
-
-
- _autoLoadSpriteFile = true;
- DataReaderHelper::getInstance()->addDataFromFile(configFilePath);
一下是void DataReaderHelper::addDataFromFile(const std::string& filePath) 的分析:
a.首先依旧是从cache机制里找一找,找到的就是已经载入过,直接放回
- for(unsigned int i = 0; i < _configFileList.size(); i++)
- {
- if (_configFileList[i] == filePath)
- {
- return;
- }
- }
- _configFileList.push_back(filePath);
b.接下来,就是判断参数的后缀是.csb二进制格式,还是文本格式,打开文件的模式不一样。
-
- _dataReaderHelper->_getFileMutex.lock();
- unsigned char *pBytes = FileUtils::getInstance()->getFileData(filePath, filemode.c_str(), &filesize);
- std::string contentStr((const char*)pBytes,filesize);
- _dataReaderHelper->_getFileMutex.unlock();
-
- DataInfo dataInfo;
-
- dataInfo.filename = filePathStr;
- dataInfo.asyncStruct = nullptr;
-
- dataInfo.baseFilePath = basefilePath;
- if (str == ".xml")
- {
- DataReaderHelper::addDataFromCache(contentStr, &dataInfo);
- }
- else if(str == ".json" || str == ".ExportJson")
- {
-
- DataReaderHelper::addDataFromJsonCache(contentStr, &dataInfo);
- }
- else if(isbinaryfilesrc)
- {
- DataReaderHelper::addDataFromBinaryCache(contentStr.c_str(),&dataInfo);
- }
在void DataReaderHelper::addDataFromJsonCache(const std::string& fileContent, DataInfo *dataInfo)中,开始解析ExportJson里的东西。过滤utf bom,解析json
紧接着是几板斧,
1)解析armatures
2)解析animations
3)解析textures
我们关注图片资源的载入方式,前2种在此略过。
-
- length = DICTOOL->getArrayCount_json(json, TEXTURE_DATA);
- for (int i = 0; i < length; i++)
- {
- const rapidjson::Value &textureDic = DICTOOL->getSubDictionary_json(json, TEXTURE_DATA, i);
-
- TextureData *textureData = decodeTexture(textureDic);
-
-
-
- if (dataInfo->asyncStruct)
- {
- _dataReaderHelper->_addDataMutex.lock();
- }
-
-
-
- ArmatureDataManager::getInstance()->addTextureData(textureData->name.c_str(), textureData, dataInfo->filename.c_str());
-
-
- textureData->release();
- if (dataInfo->asyncStruct)
- {
- _dataReaderHelper->_addDataMutex.unlock();
- }
- }
关于texture_data的json格式有哪些内容:
- {
- "name": "png/shitouren01_R_xiabi",
- "width": 83.0,
- "height": 88.0,
- "pX": 0.0,
- "pY": 1.0,
- "plistFile": ""
- },
基本对应着类TextureData
void ArmatureDataManager::addTextureData(const std::string& id, TextureData *textureData, const std::string& configFilePath)
-
- if (RelativeData *data = getRelativeData(configFilePath))
- {
-
- data->textures.push_back(id);
- }
-
-
- _textureDatas.insert(id, textureData);
在最后解析的最后,开始解析资源配置字段了,
-
- bool autoLoad = dataInfo->asyncStruct == nullptr ? ArmatureDataManager::getInstance()->isAutoLoadSpriteFile() : dataInfo->asyncStruct->autoLoadSpriteFile;
- if (autoLoad)
- {
-
- length = DICTOOL->getArrayCount_json(json, CONFIG_FILE_PATH);
- for (int i = 0; i < length; i++)
- {
- const char *path = DICTOOL->getStringValueFromArray_json(json, CONFIG_FILE_PATH, i);
- if (path == nullptr)
- {
- CCLOG("load CONFIG_FILE_PATH error.");
- return;
- }
-
- std::string filePath = path;
- filePath = filePath.erase(filePath.find_last_of("."));
-
-
- if (dataInfo->asyncStruct)
- {
- dataInfo->configFileQueue.push(filePath);
- }
- else
- {
-
-
- std::string plistPath = filePath + ".plist";
- std::string pngPath = filePath + ".png";
-
-
- ArmatureDataManager::getInstance()->addSpriteFrameFromFile((dataInfo->baseFilePath + plistPath).c_str(), (dataInfo->baseFilePath + pngPath).c_str(), dataInfo->filename.c_str());
- }
- }
- }
exprotJson里资源配置示例如下:
- "config_file_path": [
- "020.plist"
- ],
- "config_png_path": [
- "020.png"
- ]
在资源载入方法void ArmatureDataManager::addSpriteFrameFromFile(const std::string& plistPath, const std::string& imagePath, const std::string& configFilePath)里
-
- if (RelativeData *data = getRelativeData(configFilePath))
- {
- data->plistFiles.push_back(plistPath);
- }
-
-
-
- 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)
下面我逐一分析,先从原始图片资源异步加载方式开刀:
- <span style="white-space:pre"> </span>Texture2D *texture = nullptr;
-
-
- std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path);
-
-
- auto it = _textures.find(fullpath);
- if( it != _textures.end() )
- texture = it->second;
-
- if (texture != nullptr)
- {
-
- callback(texture);
- return;
- }
-
-
-
- if (_asyncStructQueue == nullptr)
- {
- _asyncStructQueue = new queue<AsyncStruct*>();
- _imageInfoQueue = new deque<ImageInfo*>();
-
-
-
-
-
- _loadingThread = new std::thread(&TextureCache::loadImage, this);
-
- _needQuit = false;
- }
-
- if (0 == _asyncRefCount)
- {
-
- Director::getInstance()->getScheduler()->schedule(schedule_selector(TextureCache::addImageAsyncCallBack), this, 0, false);
- }
-
- ++_asyncRefCount;
-
-
-
-
-
- AsyncStruct *data = new AsyncStruct(fullpath, callback);
-
-
-
- _asyncStructQueueMutex.lock();
- _asyncStructQueue->push(data);
- _asyncStructQueueMutex.unlock();
-
-
- _sleepCondition.notify_one();
结合上述的注释,基本上可以理解图片资源异步加载的基本原理了。
下面是Armature的异步加载分析:
- void ArmatureDataManager::addArmatureFileInfoAsync(const std::string& configFilePath, Ref *target, SEL_SCHEDULE selector)
- {
-
- addRelativeData(configFilePath);
-
-
- _autoLoadSpriteFile = true;
-
-
- DataReaderHelper::getInstance()->addDataFromFileAsync("", "", configFilePath, target, selector);
- }
- for(unsigned int i = 0; i < _configFileList.size(); i++)
- {
- if (_configFileList[i] == filePath)
- {
- if (target && selector)
- {
- if (_asyncRefTotalCount == 0 && _asyncRefCount == 0)
- {
- (target->*selector)(1);
- }
- else
- {
- (target->*selector)((_asyncRefTotalCount - _asyncRefCount) / (float)_asyncRefTotalCount);
- }
- }
- return;
- }
- }
- _configFileList.push_back(filePath);
一开始,依旧是从cache里查找一下,看是不是已经加载了,加载了,则调用下回调函数,这里的统计任务个数,后面会讲到,将本加载配置文件加入cache中。
-
-
- if (_asyncStructQueue == nullptr)
- {
- _asyncStructQueue = new std::queue<AsyncStruct *>();
- _dataQueue = new std::queue<DataInfo *>();
-
-
-
-
-
- _loadingThread = new std::thread(&DataReaderHelper::loadData, this);
-
- need_quit = false;
- }
-
- if (0 == _asyncRefCount)
- {
-
-
- Director::getInstance()->getScheduler()->schedule(schedule_selector(DataReaderHelper::addDataAsyncCallBack), this, 0, false);
- }
-
-
- ++_asyncRefCount;
- ++_asyncRefTotalCount;
-
-
- if (target)
- {
- target->retain();
- }
- void DataReaderHelper::addDataAsyncCallBack(float dt)
- {
- ...
-
-
- DataInfo *pDataInfo = dataQueue->front();
- dataQueue->pop();
- _dataInfoMutex.unlock();
-
- AsyncStruct *pAsyncStruct = pDataInfo->asyncStruct;
-
-
-
-
- if (pAsyncStruct->imagePath != "" && pAsyncStruct->plistPath != "")
- {
- _getFileMutex.lock();
- ArmatureDataManager::getInstance()->addSpriteFrameFromFile(pAsyncStruct->plistPath.c_str(), pAsyncStruct->imagePath.c_str(), pDataInfo->filename.c_str());
- _getFileMutex.unlock();
- }
-
-
- while (!pDataInfo->configFileQueue.empty())
- {
- std::string configPath = pDataInfo->configFileQueue.front();
- _getFileMutex.lock();
-
- <span style="white-space:pre"> </span>
- 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_SCHEDULE selector = pAsyncStruct->selector;
-
-
- --_asyncRefCount;
-
-
- if (target && selector)
- {
-
- (target->*selector)((_asyncRefTotalCount - _asyncRefCount) / (float)_asyncRefTotalCount);
-
- target->release();
- }
-
-
- delete pAsyncStruct;
- delete pDataInfo;
-
-
- if (0 == _asyncRefCount)
- {
- _asyncRefTotalCount = 0;
- Director::getInstance()->getScheduler()->unschedule(schedule_selector(DataReaderHelper::addDataAsyncCallBack), this);
- }
从上面的分析,我们可以看出Armature的异步加载,只是部分,而不是全部,只是把解析部分交给了线程,图片资源还是需要自己通过图片资源异步加载方式加载。
转载地址:http://hvsmi.baihongyu.com/