CCSpriteFrameCache精灵帧缓存分析

CCSpriteFrameCache精灵帧缓存分析(1):
1、单例类
CCSpriteFrameCache* CCSpriteFrameCache::sharedSpriteFrameCache(void)
{
    if (! pSharedSpriteFrameCache)
    {
        pSharedSpriteFrameCache = new CCSpriteFrameCache();
        pSharedSpriteFrameCache->init();
    }

    return pSharedSpriteFrameCache;
}

2、常用方法分析:
void CCSpriteFrameCache::addSpriteFramesWithFile(const char *pszPlist)
{
    CCAssert(pszPlist, "plist filename should not be NULL");
	
    //文件缓存m_pLoadedFileNames,如果已经加载过,则不再加载
    if (m_pLoadedFileNames->find(pszPlist) == m_pLoadedFileNames->end())
    {
        std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(pszPlist);

	//因为xx.plist文件的根节点<dict>,所以使用createWithContentsOfFileThreadSafe方法
	//进行解析
        CCDictionary *dict = CCDictionary::createWithContentsOfFileThreadSafe(fullPath.c_str());

        string texturePath("");

        /*
		<key>metadata</key>
		<dict>
		    <key>format</key>
		    <integer>2</integer>
		    <key>realTextureFileName</key>
		    <string>digit03.pvr.ccz</string>
		    <key>size</key>
		    <string>{128,256}</string>
		    <key>smartupdate</key>
		    <string>$TexturePacker:SmartUpdate:3c8e7a7da2ef2a8189712fc559ff0750:163e3804f74ef566f6194c8708efc6b9:d14ec1949f1c567f2820ca7d65c7607d$</string>
		    <key>textureFileName</key>
		    <string>digit03.pvr.ccz</string>
		</dict>
	*/
        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 如果纹理名不为空,则通过pszPlist文件的相对路径,获得纹理文件的全路径
            texturePath = CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(texturePath.c_str(), pszPlist);
        }
        else
        {
	    //如果texturePath为空,则把pszPlist的后缀替换成.png,作为纹理图片名
            // 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());
        }
        
	//加载纹理
        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();
    }

}

---addSpriteFramesWithDictionary-->>
void CCSpriteFrameCache::addSpriteFramesWithDictionary(CCDictionary* dictionary, CCTexture2D *pobTexture)
{
    /*
    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+
    */

    CCDictionary *metadataDict = (CCDictionary*)dictionary->objectForKey("metadata");

    //存储小图片信息的字典
    CCDictionary *framesDict = (CCDictionary*)dictionary->objectForKey("frames");
    int format = 0;

    // get the format
    // 获取图片打包的格式,版本
    if(metadataDict != NULL) 
    {
        format = metadataDict->valueForKey("format")->intValue();
    }

    // check the format
    CCAssert(format >=0 && format <= 3, "format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:textureFilename:");

    //遍历小图片信息字典,根据小图片信息生成存储CCSpriteFrame的字典
    CCDictElement* pElement = NULL;
    CCDICT_FOREACH(framesDict, pElement)
    {
        /*
	    <key>digit03_00.png</key>
            <dict>
                <key>frame</key>
                <string>{{2,70},{32,44}}</string>
                <key>offset</key>
                <string>{0,0}</string>
                <key>rotated</key>
                <true/>
                <key>sourceColorRect</key>
                <string>{{0,0},{32,44}}</string>
                <key>sourceSize</key>
                <string>{32,44}</string>
            </dict>
	*/
        CCDictionary* frameDict = (CCDictionary*)pElement->getObject();
        std::string spriteFrameName = pElement->getStrKey();
        CCSpriteFrame* spriteFrame = (CCSpriteFrame*)m_pSpriteFrames->objectForKey(spriteFrameName);
        if (spriteFrame)
        {
            continue;
        }
        
	//根据类型进行不同的解析,最终都会根据图片信息生成CCSpriteFrame,
	//并且存放到m_pSpriteFrames缓存中,那我们在创建精灵时,就可以
	//使用这些CCSpriteFrame去创建,这样就可以把很多小图打包成大图纹理,
	//优化纹理的使用。
        if(format == 0) 
        {
            float x = frameDict->valueForKey("x")->floatValue();
            float y = frameDict->valueForKey("y")->floatValue();
            float w = frameDict->valueForKey("width")->floatValue();
            float h = frameDict->valueForKey("height")->floatValue();
            float ox = frameDict->valueForKey("offsetX")->floatValue();
            float oy = frameDict->valueForKey("offsetY")->floatValue();
            int ow = frameDict->valueForKey("originalWidth")->intValue();
            int oh = frameDict->valueForKey("originalHeight")->intValue();
            // check ow/oh
            if(!ow || !oh)
            {
                CCLOGWARN("cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenrate the .plist");
            }
            // abs ow/oh
            ow = abs(ow);
            oh = abs(oh);
            // create frame
            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) 
        {
            CCRect frame = CCRectFromString(frameDict->valueForKey("frame")->getCString());
            bool rotated = false;

            // rotation
            if (format == 2)
            {
                rotated = frameDict->valueForKey("rotated")->boolValue();
            }

            CCPoint offset = CCPointFromString(frameDict->valueForKey("offset")->getCString());
            CCSize sourceSize = CCSizeFromString(frameDict->valueForKey("sourceSize")->getCString());

            // create frame
            spriteFrame = new CCSpriteFrame();
            spriteFrame->initWithTexture(pobTexture, 
                frame,
                rotated,
                offset,
                sourceSize
                );
        } 
        else if (format == 3)
        {
            // get values
            CCSize spriteSize = CCSizeFromString(frameDict->valueForKey("spriteSize")->getCString());
            CCPoint spriteOffset = CCPointFromString(frameDict->valueForKey("spriteOffset")->getCString());
            CCSize spriteSourceSize = CCSizeFromString(frameDict->valueForKey("spriteSourceSize")->getCString());
            CCRect textureRect = CCRectFromString(frameDict->valueForKey("textureRect")->getCString());
            bool textureRotated = frameDict->valueForKey("textureRotated")->boolValue();

            // get aliases
            CCArray* aliases = (CCArray*) (frameDict->objectForKey("aliases"));
            CCString * frameKey = new CCString(spriteFrameName);

            CCObject* pObj = NULL;
            CCARRAY_FOREACH(aliases, pObj)
            {
                std::string oneAlias = ((CCString*)pObj)->getCString();
                if (m_pSpriteFramesAliases->objectForKey(oneAlias.c_str()))
                {
                    CCLOGWARN("cocos2d: WARNING: an alias with name %s already exists", oneAlias.c_str());
                }

                m_pSpriteFramesAliases->setObject(frameKey, oneAlias.c_str());
            }
            frameKey->release();
            // create frame
            spriteFrame = new CCSpriteFrame();
            spriteFrame->initWithTexture(pobTexture,
                            CCRectMake(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height),
                            textureRotated,
                            spriteOffset,
                            spriteSourceSize);
        }

        // add sprite frame
        m_pSpriteFrames->setObject(spriteFrame, spriteFrameName);
        spriteFrame->release();
    }
}



CCSpriteFrameCache精灵帧缓存分析(2):
CCSpriteFrameCache其他方法:
1、根据plist和纹理图片名加载
    /** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames.
     @since v0.99.5
     @js addSpriteFrames
     */
    void addSpriteFramesWithFile(const char* plist, const char* textureFileName);
   
2、
// Purges the dictionary of loaded sprite frames.
// 移除所有的精灵帧缓存
void CCSpriteFrameCache::removeSpriteFrames(void)


3、 
    /** Removes unused sprite frames.
     * Sprite Frames that have a retain count of 1 will be deleted.
     * It is convenient to call this method after when starting a new Scene.
     */
     //移除不使用的精灵帧缓存,什么不使用的精灵帧,就是引用计数为1,因为
     //我们加入到精灵帧缓存中的精灵帧的应用计数为1,如果被其他使用,
     //则引用计数就会增加,使用完后,引用计数就会减1,所以如果引用计数为1,就表明
     //此时没有被其他东西引用。
    void removeUnusedSpriteFrames(void);


    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;
        }
    }


    //如果有一个精灵帧被移除,那么因为我们不知道被移除的精灵帧属于那个.plist文件,所以
    //需要把所有缓存的.plist文件清空。但是精灵帧缓存还是存在的。
    // 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();
    }
}


4、
    /** Removes multiple Sprite Frames from a plist file.
    * Sprite Frames stored in this file will be removed.
    * It is convenient to call this method when a specific texture needs to be removed.
    * @since v0.99.5
    */
    void removeSpriteFramesFromFile(const char* plist);
   //移除plist文件中包含的所有精灵帧。
   void CCSpriteFrameCache::removeSpriteFramesFromFile(const char* plist)
{
    std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(plist);
    CCDictionary* dict = CCDictionary::createWithContentsOfFileThreadSafe(fullPath.c_str());


    //移除dict中包含的所有精灵帧
    removeSpriteFramesFromDictionary((CCDictionary*)dict);


    // remove it from the cache
    set<string>::iterator ret = m_pLoadedFileNames->find(plist);
    if (ret != m_pLoadedFileNames->end())
    {
        //把plist文件从m_pLoadedFileNames缓存文件中清楚。
        m_pLoadedFileNames->erase(ret);
    }


    dict->release();
}


5、
/** Removes all Sprite Frames associated with the specified textures.
    * It is convenient to call this method when a specific texture needs to be removed.
    * @since v0.995.
    */
    根据纹理移除精灵帧缓存,因为同一个plist文件中所有的精灵帧对应同一个纹理,
    这里只需要
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);
}


6、
     /** Returns an Sprite Frame that was previously added.
     If the name is not found it will return nil.
     You should retain the returned copy if you are going to use it.
     @js getSpriteFrame
     */
    CCSpriteFrame* spriteFrameByName(const char *pszName);
    //通过文件名获取对应的CCSpriteFrame*。
    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;
}


CCSpriteFrameCache精灵帧缓存分析(3):
上面两篇中已经对CCSpriteFrameCache进行了分析,CCSpriteFrameCache就是对精灵
帧进行缓存,那么精灵帧是什么呢?我们这一篇来分析一下:
1、
    /** @brief A CCSpriteFrame has:
    //这里说明精灵帧拥有两个东西:(1)纹理,(2)在纹理坐标和大小
    - texture: A CCTexture2D that will be used by the CCSprite
    - rectangle: A rectangle of the texture
    继承自class CC_DLL CCSpriteFrame : public CCObject
 
2、
在CCSpriteFrameCache中都是通过下面这个方法初始化精灵帧的:
	    /*
	    // create frame
            spriteFrame = new CCSpriteFrame();
            spriteFrame->initWithTexture(pobTexture, 
                frame,
                rotated,
                offset,
                sourceSize
                );
	   */

bool CCSpriteFrame::initWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
{
    m_pobTexture = pobTexture;

    if (pobTexture)
    {
        //对纹理的引用计数加1
        pobTexture->retain();
    }
    //从这里可以看到,其实CCSpriteFrame精灵帧就是包含了纹理,纹理坐标和大小,旋转等
    //信息的一个类,拥有这些信息,我们就可以使用精灵帧来创建精灵了。
    m_obRectInPixels = rect;
    m_obRect = CC_RECT_PIXELS_TO_POINTS(rect);
    m_obOffsetInPixels = offset;
    m_obOffset = CC_POINT_PIXELS_TO_POINTS( m_obOffsetInPixels );
    m_obOriginalSizeInPixels = originalSize;
    m_obOriginalSize = CC_SIZE_PIXELS_TO_POINTS( m_obOriginalSizeInPixels );
    m_bRotated = rotated;

    return true;
}

3、使用精灵帧创建精灵:
     /**
     * Creates a sprite with an sprite frame.
     *
     * @param   pSpriteFrame    A sprite frame which involves a texture and a rect
     * @return  A valid sprite object that is marked as autoreleased.
     */
    static CCSprite* createWithSpriteFrame(CCSpriteFrame *pSpriteFrame);

    ----->>>
    CCSprite* CCSprite::createWithSpriteFrame(CCSpriteFrame *pSpriteFrame)
{
    CCSprite *pobSprite = new CCSprite();
    if (pSpriteFrame && pobSprite && pobSprite->initWithSpriteFrame(pSpriteFrame))
    {
        pobSprite->autorelease();
        return pobSprite;
    }
    CC_SAFE_DELETE(pobSprite);
    return NULL;
}

---->>>
bool CCSprite::initWithSpriteFrame(CCSpriteFrame *pSpriteFrame)
{
    CCAssert(pSpriteFrame != NULL, "");
    //使用精灵帧中的纹理创建精灵
    bool bRet = initWithTexture(pSpriteFrame->getTexture(), pSpriteFrame->getRect());
    setDisplayFrame(pSpriteFrame);

    return bRet;
}

4、使用缓存过的精灵帧创建精灵:
    /**
     * Initializes a sprite with an sprite frame name.
     *
     * A CCSpriteFrame will be fetched from the CCSpriteFrameCache by name.
     * If the CCSpriteFrame doesn't exist it will raise an exception.
     *
     * @param   pszSpriteFrameName  A key string that can fected a volid CCSpriteFrame from CCSpriteFrameCache
     * @return  true if the sprite is initialized properly, false otherwise.
     */
    virtual bool initWithSpriteFrameName(const char *pszSpriteFrameName);

5、通过图片文件名创建精灵帧,但是必须传入一个纹理坐标和大小
 /** Create a CCSpriteFrame with a texture filename, rect in points.
     It is assumed that the frame was not trimmed.
     */
    static CCSpriteFrame* create(const char* filename, const CCRect& rect);

---->>>>>
    CCSpriteFrame* CCSpriteFrame::create(const char* filename, const CCRect& rect)
{
    CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();;
    pSpriteFrame->initWithTextureFilename(filename, rect);
    pSpriteFrame->autorelease();

    return pSpriteFrame;
}

---->>>
这里可以看到并没有加载纹理,那么在什么时候加载纹理呢?
bool CCSpriteFrame::initWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
{
    m_pobTexture = NULL;
    m_strTextureFilename = filename;
    m_obRectInPixels = rect;
    m_obRect = CC_RECT_PIXELS_TO_POINTS( rect );
    m_obOffsetInPixels = offset;
    m_obOffset = CC_POINT_PIXELS_TO_POINTS( m_obOffsetInPixels );
    m_obOriginalSizeInPixels = originalSize;
    m_obOriginalSize = CC_SIZE_PIXELS_TO_POINTS( m_obOriginalSizeInPixels );
    m_bRotated = rotated;

    return true;
}
---->>>在获取纹理的时候加载纹理:
CCTexture2D* CCSpriteFrame::getTexture(void)
{
    if( m_pobTexture ) {
        return m_pobTexture;
    }

    //加载纹理
    if( m_strTextureFilename.length() > 0 ) {
        return CCTextureCache::sharedTextureCache()->addImage(m_strTextureFilename.c_str());
    }
    // no texture or texture filename
    return NULL;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值