cocos2d的缓存机制主要设置到CCTextureCache、CCSpriteFrameCache、CCAnimationCache。下面来简单介绍一下这三种缓存机制。
CCTextureCache
游戏还没运行时我们的图片资源都是保存在外部存储器(硬盘、闪存)中,当我们运行游戏,把图片显示出来时,这个流程就是从外部存储器读取图片数据到内存,然后根据内存数据进行渲染,这整个步骤比较耗时的操作是从外部存储器读取数据到内存的过程。你想在一瞬间显示大量的图片,如果采用的是每张图片顺序执行(内部存储-内存-渲染),那么会发现这些图片展现出来会有明显的时间差,会给人卡顿的感觉。所以一般的游戏在打开游戏的时候都会有加载资源的界面,在这个界面把大量需要在下一个界面显示的资源从内部存储器读取到内存(这个就是所谓的缓存),最后在需要显示的的时候才根据内存数据进行渲染。
Texture2D * TextureCache::addImage(const std::string &path)
{
Texture2D * texture = nullptr;
Image* image = nullptr;
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;
if (! texture)
{
// all images are handled by UIImage except PVR extension that is handled by our own handler
do
{
image = new (std::nothrow) Image();
CC_BREAK_IF(nullptr == image);
bool bRet = image->initWithImageFile(fullpath);
CC_BREAK_IF(!bRet);
texture = new (std::nothrow) Texture2D();
if( texture && texture->initWithImage(image) )
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
// cache the texture file name
VolatileTextureMgr::addImageTexture(texture, fullpath);
#endif
// texture already retained, no need to re-retain it
_textures.insert( std::make_pair(fullpath, texture) );
}
} while (0);
}
CC_SAFE_RELEASE(image);
return texture;
}
从上面代码可以看到,addImage(const std::string &path)方法根据传入的图片地址,返回一个已存在的texture或新生成一个texture并返回。
通过下面Sprite的create方法可以看到里面也调用了addImage方法:
bool Sprite::initWithFile(const std::string& filename)
{
CCASSERT(filename.size()>0, "Invalid filename for sprite");
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
if (texture)
{
Rect rect = Rect::ZERO;
rect.size = texture->getContentSize();
return initWithTexture(texture, rect);
}
return false;
}
CCSpriteFrameCache
这个类是用来缓存有多张碎图合成的大图,主要通过addSpriteFramesWithFile方法将图片载入内存生成Texture对象
void SpriteFrameCache::addSpriteFramesWithFile(const std::string& plist)
{
CCASSERT(plist.size()>0, "plist filename should not be nullptr");
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();
// try to read texture file name from meta data
texturePath = metadataDict["textureFileName"].asString();
}
if (!texturePath.empty())
{
// build texture path relative to plist file
texturePath = FileUtils::getInstance()->fullPathFromRelativeFile(texturePath.c_str(), plist);
}
else
{
// build texture path by replacing file extension
texturePath = plist;
// remove .xxx
size_t startPos = texturePath.find_last_of(".");
texturePath = texturePath.erase(startPos);
// append .png
texturePath = texturePath.append(".png");
}
//根据得到的图片路径生成texture对象
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(texturePath.c_str());
if (texture)
{
addSpriteFramesWithDictionary(dict, texture);
_loadedFileNames->insert(plist);
}
}
}
然后根据plist文件和texture对象,生成相对于的spriteFrame对象。主要代码如下:
void SpriteFrameCache::addSpriteFramesWithDictionary(ValueMap& dictionary, Texture2D* texture)
{
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;
}
if(format == 0)
{
// create frame
spriteFrame = SpriteFrame::createWithTexture(texture,
Rect(x, y, w, h),
false,
Vec2(ox, oy),
Size((float)ow, (float)oh)
);
}
else if(format == 1 || format == 2)
{
// create frame
spriteFrame = SpriteFrame::createWithTexture(texture,
frame,
rotated,
offset,
sourceSize
);
}
else if (format == 3)
{
// create frame
spriteFrame = SpriteFrame::createWithTexture(texture,
Rect(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height),
textureRotated,
spriteOffset,
spriteSourceSize);
}
// add sprite frame
_spriteFrames.insert(spriteFrameName, spriteFrame);
}
}
最后创建sprite时可以用Sprite* Sprite::createWithSpriteFrameName(const std::string& spriteFrameName)创建sprite
CCAnimationCache
这个用法也是比较简单,主要是将动画缓存起来,需要用的时候再调用
void AnimationCache::addAnimation(Animation *animation, const std::string& name)
{
_animations.insert(name, animation);
}
Animation* AnimationCache::getAnimation(const std::string& name)
{
return _animations.at(name);
}