CCSpriteFrameCache可以通过读入plist文件来快速加载精灵帧,分析代码后发现它内部使用字典来存储的,代码比较清晰,这里备份下大致的代码实现:
CONTENTS
Plist文件实例:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>frames</key> <dict> <key>00.png</key> <dict> <key>frame</key> <string>{{2,2},{134,132}}</string> <key>offset</key> <string>{0,0}</string> <key>rotated</key> <false/> <key>sourceColorRect</key> <string>{{0,0},{134,132}}</string> <key>sourceSize</key> <string>{134,132}</string> </dict> 。。。。。 。。。。。 </dict> <key>metadata</key> <dict> <key>format</key> <integer>2</integer> <key>realTextureFileName</key> <string>disappear.png</string> <key>size</key> <string>{512,512}</string> <key>smartupdate</key> <string>$TexturePacker:SmartUpdate:93e379cfaf8b4f93761672030328e9d5:8a687b11806418a6a0a11123f2513565:6a8372f3fd06f837d4fb70cde4278252$</string> <key>textureFileName</key> <string>disappear.png</string> </dict> </dict> </plist>
PLIST的框架:
顶层字典: key:frames value: { 二级字典A: key:00.png value: { 三级字典: 精灵帧的宽高,大小等属性 } key:01.png value: { 三级字典: 精灵帧的宽高,大小等属性 } } key:metadata value: { 二级字典B: plist文件的各种属性。 }
代码流程和框架:
//单例及其初始化 static CCSpriteFrameCache *pSharedSpriteFrameCache = NULL; CCSpriteFrameCache* CCSpriteFrameCache::sharedSpriteFrameCache(void) { if (! pSharedSpriteFrameCache) { pSharedSpriteFrameCache = new CCSpriteFrameCache(); pSharedSpriteFrameCache->init(); } return pSharedSpriteFrameCache; } void CCSpriteFrameCache::purgeSharedSpriteFrameCache(void) { CC_SAFE_RELEASE_NULL(pSharedSpriteFrameCache); } bool CCSpriteFrameCache::init(void) { //存放精灵帧的字典,key:精灵帧的名称("00.png"),value:精灵帧实例 m_pSpriteFrames= new CCDictionary(); //存放精灵帧别名的字典,在plist的format=3时会出现,这里format=2,没有作用。 m_pSpriteFramesAliases = new CCDictionary(); //已经读入的plist文件的set,防止重复读入。 m_pLoadedFileNames = new std::set<std::string>(); return true; } CCSpriteFrameCache::~CCSpriteFrameCache(void) { 。。。。。 } void CCSpriteFrameCache::addSpriteFramesWithDictionary(CCDictionary* dictionary, CCTexture2D *pobTexture) { //根据plist框架可知 metadataDict为二级字典B,framesDict为二级字典A。 CCDictionary *metadataDict = (CCDictionary*)dictionary->objectForKey("metadata"); CCDictionary *framesDict = (CCDictionary*)dictionary->objectForKey("frames"); int format = 0; // get the format if(metadataDict != NULL) { //获取plist的format值,来确认plist的版本,以便确定三级字典的格式。 format = metadataDict->valueForKey("format")->intValue(); } // check the format CCAssert(format >=0 && format <= 3, "format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:textureFilename:"); CCDictElement* pElement = NULL; CCDICT_FOREACH(framesDict, pElement) { //frameDict为三级字典 CCDictionary* frameDict = (CCDictionary*)pElement->getObject(); std::string spriteFrameName = pElement->getStrKey(); CCSpriteFrame* spriteFrame = (CCSpriteFrame*)m_pSpriteFrames->objectForKey(spriteFrameName); if (spriteFrame) { //如果已经有了该名称的精灵帧,那么直接跳过 continue; } //根据format来从三级字典中获取属性信息。 if(format == 0) { 。。。。。 。。。。。 //根据属性信息和pobTexture创建精灵帧 spriteFrame = new CCSpriteFrame(); spriteFrame->initWithTexture(pobTexture, CCRectMake(x, y, w, h), false, CCPointMake(ox, oy), CCSizeMake((float)ow, (float)oh) ); } else if(format == 1 || format == 2) { 。。。。。 。。。。。 //根据属性信息和pobTexture创建精灵帧 // create frame spriteFrame = new CCSpriteFrame(); spriteFrame->initWithTexture(pobTexture, frame, rotated, offset, sourceSize ); } else if (format == 3) { //格式为3时有alias的设置 。。。。。 。。。。。 } //加入精灵帧字典 // add sprite frame m_pSpriteFrames->setObject(spriteFrame, spriteFrameName); spriteFrame->release(); } } void CCSpriteFrameCache::addSpriteFramesWithFile(const char *pszPlist, CCTexture2D *pobTexture) { //根据plist的文件路径获取plist的内容 std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(pszPlist); CCDictionary *dict = CCDictionary::createWithContentsOfFileThreadSafe(fullPath.c_str()); //将字典和texture传入函数 addSpriteFramesWithDictionary(dict, pobTexture); dict->release(); } void CCSpriteFrameCache::addSpriteFramesWithFile(const char* plist, const char* textureFileName) { //根据texture的路径获取texture的内容 CCAssert(textureFileName, "texture name should not be null"); CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage(textureFileName); //将plist和texture传入函数 if (texture) { addSpriteFramesWithFile(plist, texture); } 。。。。。 } void CCSpriteFrameCache::addSpriteFramesWithFile(const char *pszPlist) { CCAssert(pszPlist, "plist filename should not be NULL"); //检查是否已经读过该plist文件 if (m_pLoadedFileNames->find(pszPlist) == m_pLoadedFileNames->end()) { std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(pszPlist); CCDictionary *dict = CCDictionary::createWithContentsOfFileThreadSafe(fullPath.c_str()); string texturePath(""); //根据plist文件的路径和plist文件中的textureFileName属性来推断出textrue文件的位置。 CCDictionary* metadataDict = (CCDictionary*)dict->objectForKey("metadata"); if (metadataDict) { // try to read texture file name from meta data texturePath = metadataDict->valueForKey("textureFileName")->getCString(); } if (! texturePath.empty()) { // build texture path relative to plist file texturePath = CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(texturePath.c_str(), pszPlist); } else { // build texture path by replacing file extension texturePath = pszPlist; // remove .xxx size_t startPos = texturePath.find_last_of("."); texturePath = texturePath.erase(startPos); // append .png texturePath = texturePath.append(".png"); CCLOG("cocos2d: CCSpriteFrameCache: Trying to use file %s as texture", texturePath.c_str()); } //读取textrue,将texture和plist内容传入函数,个人该觉冗余代码。 CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(texturePath.c_str()); if (pTexture) { addSpriteFramesWithDictionary(dict, pTexture); m_pLoadedFileNames->insert(pszPlist); } else { CCLOG("cocos2d: CCSpriteFrameCache: Couldn't load texture"); } dict->release(); } } void CCSpriteFrameCache::addSpriteFrame(CCSpriteFrame *pobFrame, const char *pszFrameName) { //直接添加spriteframe到字典中 m_pSpriteFrames->setObject(pobFrame, pszFrameName); } //清空字典 void CCSpriteFrameCache::removeSpriteFrames(void) { m_pSpriteFrames->removeAllObjects(); m_pSpriteFramesAliases->removeAllObjects(); m_pLoadedFileNames->clear(); } //清理无用的,判断标准为引用计数是否为1 void CCSpriteFrameCache::removeUnusedSpriteFrames(void) { bool bRemoved = false; CCDictElement* pElement = NULL; CCDICT_FOREACH(m_pSpriteFrames, pElement) { CCSpriteFrame* spriteFrame = (CCSpriteFrame*)pElement->getObject(); if( spriteFrame->retainCount() == 1 ) { CCLOG("cocos2d: CCSpriteFrameCache: removing unused frame: %s", pElement->getStrKey()); m_pSpriteFrames->removeObjectForElememt(pElement); bRemoved = true; } } // XXX. Since we don't know the .plist file that originated the frame, we must remove all .plist from the cache if( bRemoved ) { m_pLoadedFileNames->clear(); } } //根据精灵帧的名称删除spriteframe,注意由于不知道这个名称属于哪个plist文件,因此需要清空plist文件读入set。 void CCSpriteFrameCache::removeSpriteFrameByName(const char *pszName) { // explicit nil handling if( ! pszName ) { return; } // Is this an alias ? CCString* key = (CCString*)m_pSpriteFramesAliases->objectForKey(pszName); if (key) { m_pSpriteFrames->removeObjectForKey(key->getCString()); m_pSpriteFramesAliases->removeObjectForKey(key->getCString()); } else { m_pSpriteFrames->removeObjectForKey(pszName); } // XXX. Since we don't know the .plist file that originated the frame, we must remove all .plist from the cache m_pLoadedFileNames->clear(); } //根据plist文件删除spriteframe void CCSpriteFrameCache::removeSpriteFramesFromFile(const char* plist) { std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(plist); CCDictionary* dict = CCDictionary::createWithContentsOfFileThreadSafe(fullPath.c_str()); removeSpriteFramesFromDictionary((CCDictionary*)dict); // remove it from the cache set<string>::iterator ret = m_pLoadedFileNames->find(plist); if (ret != m_pLoadedFileNames->end()) { m_pLoadedFileNames->erase(ret); } dict->release(); } void CCSpriteFrameCache::removeSpriteFramesFromDictionary(CCDictionary* dictionary) { 。。。。。 } //根据精灵帧删除spriteframe,个人感觉有bug:由于不知道这个名称属于哪个plist文件,因此需要清空plist文件读入set。 void CCSpriteFrameCache::removeSpriteFramesFromTexture(CCTexture2D* texture) { CCArray* keysToRemove = CCArray::create(); CCDictElement* pElement = NULL; CCDICT_FOREACH(m_pSpriteFrames, pElement) { string key = pElement->getStrKey(); CCSpriteFrame* frame = (CCSpriteFrame*)m_pSpriteFrames->objectForKey(key.c_str()); if (frame && (frame->getTexture() == texture)) { keysToRemove->addObject(CCString::create(pElement->getStrKey())); } } m_pSpriteFrames->removeObjectsForKeys(keysToRemove); } //根据精灵帧名称查询字典,获取精灵帧 CCSpriteFrame* CCSpriteFrameCache::spriteFrameByName(const char *pszName) { CCSpriteFrame* frame = (CCSpriteFrame*)m_pSpriteFrames->objectForKey(pszName); if (!frame) { // try alias dictionary CCString *key = (CCString*)m_pSpriteFramesAliases->objectForKey(pszName); if (key) { frame = (CCSpriteFrame*)m_pSpriteFrames->objectForKey(key->getCString()); if (! frame) { CCLOG("cocos2d: CCSpriteFrameCache: Frame '%s' not found", pszName); } } } return frame; }