plist创建sprite的过程
通过以下2句代码,可以利用plist创建一个sprite,下面将详细分析其过程。
// 通过plist载入缓存
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("test.plist");
// 通过缓存载入sprite
CCSprite* sp = CCSprite::createWithSpriteFrameName("test01.png");
通过plist载入缓存
首先,我们来分析通过plist载入缓存的过程。跟踪源码可以看到如下代码片段:
void CCSpriteFrameCache::addSpriteFramesWithFile(const char *pszPlist)
{
CCAssert(pszPlist, "plist filename should not be NULL");
// 判断是否加载过该文件,如果没加载过,才执行以下操作
if (m_pLoadedFileNames->find(pszPlist) == m_pLoadedFileNames->end())
{
// 获取完整路径名,创建dict
std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(pszPlist);
CCDictionary *dict = CCDictionary::createWithContentsOfFileThreadSafe(fullPath.c_str());
std::string texturePath("");
// 尝试获取textureFileName,如果需要指定,格式如下:
// metadata
// textureFileName
// tex.png
// 指定载入tex.png
CCDictionary* metadataDict = (CCDictionary*)dict->objectForKey("metadata");
if (metadataDict)
{
// 尝试从元数据中读取纹理文件名
texturePath = metadataDict->valueForKey("textureFileName")->getCString();
}
// 如果有指定tex文件,则直接查找完整路径
if (!texturePath.empty())
{
// 构建相对于plist文件的纹理路径
texturePath = CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(texturePath.c_str(), pszPlist);
}
else // 没有指定tex文件,载入plist对应的png,例如plist文件名:abc.plist,则载入的png为:abc.png
{
// 通过替换文件扩展名构建纹理路径
texturePath = pszPlist;
// 移除文件扩展名
size_t startPos = texturePath.find_last_of(".");
texturePath = texturePath.erase(startPos);
// 追加.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)
{
// 载入spriteFrames
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)
{
/*
// 支持4种格式
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+
*/
// 分离两个dict
CCDictionary *metadataDict = (CCDictionary*)dictionary->objectForKey("metadata");
CCDictionary *framesDict = (CCDictionary*)dictionary->objectForKey("frames");
int format = 0;
// 从metadata获取格式,默认为0
if(metadataDict != NULL)
{
format = metadataDict->valueForKey("format")->intValue();
}
// 检测format,支持0 - 3
CCAssert(format >=0 && format <= 3, "format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:textureFilename:");
// 遍历frames
CCDictElement* pElement = NULL;
CCDICT_FOREACH(framesDict, pElement)
{
CCDictionary* frameDict = (CCDictionary*)pElement->getObject();
std::string spriteFrameName = pElement->getStrKey(); // 取key作为sprite名
// 如果已经加载过,不再处理
CCSpriteFrame* spriteFrame = (CCSpriteFrame*)m_pSpriteFrames->objectForKey(spriteFrameName);
if (spriteFrame)
{
continue;
}
// 根据format处理
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();
// 检查ow/oh
if(!ow || !oh)
{
CCLOGWARN("cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenrate the .plist");
}
// 取ow/oh的绝对值
ow = abs(ow);
oh = abs(oh);
// 创建帧
spriteFrame = new CCSpriteFrame();
// 对照以下初始化函数,大概就明白了
// initWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
spriteFrame->initWithTexture(pobTexture,
CCRectMake(x, y, w, h),
false,
CCPointMake(ox, oy),
CCSizeMake((float)ow, (float)oh));
}
// 以下类似
// 加入缓存,名字用spriteFrameName,m_pSpriteFrames是一个dict
m_pSpriteFrames->setObject(spriteFrame, spriteFrameName);
spriteFrame->release();
}
}
这部分的大概流程为:载入plist文件,载入对应的纹理文件,根据plist文件中的信息选择纹理的区域,载入spriteFrame,并将其加入到缓存中。
通过缓存载入sprite
下面是通过缓存载入sprite的代码:
CCSprite* CCSprite::createWithSpriteFrameName(const char *pszSpriteFrameName)
{
// 从缓存里取对应名字的spriteFrame
CCSpriteFrame *pFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(pszSpriteFrameName);
#if COCOS2D_DEBUG > 0
char msg[256] = {0};
sprintf(msg, "Invalid spriteFrameName: %s", pszSpriteFrameName);
CCAssert(pFrame != NULL, msg);
#endif
return createWithSpriteFrame(pFrame);
}
该函数的作用是从缓存中获取指定名称的 spriteFrame,如果获取成功,则使用该 spriteFrame 创建一个 CCSprite 对象。