本文为xiedantibu原创,转载请注明作者及出处!
###目录
—-
SpriteFrame
SpriteFrameCache
SpriteFrameCache与TextureCache
plist文件格式
COCOS2D-X常用宏
C相关方法学习
####SpriteFrame-精灵帧 —
SpriteFrame我们查看下其源码,发现其持有一个Texture2D *_texture
以及相关的纹理Name,以及纹理偏移量等。那SpriteFrame的主要作用其实也就是为了保存从plist文件中读取的纹理信息,配合SpriteFrameCache实现精灵帧缓存的作用。下面是SpriteFrame的相关源码:
构造SpriteFrame
static SpriteFrame* create(const std::string& filename, const Rect& rect);
static SpriteFrame* create(const std::string& filename, const Rect& rect, bool rotated, const Vec2& offset, const Size& originalSize);//很少用
static SpriteFrame* createWithTexture(Texture2D* pobTexture, const Rect& rect);//通过纹理Texture2D以及Rect创建精灵帧
static SpriteFrame* createWithTexture(Texture2D* pobTexture, const Rect& rect, bool rotated, const Vec2& offset, const Size& originalSize);
以下为初始化精灵帧的主要方法
bool initWithTexture(Texture2D* pobTexture, const Rect& rect);
bool initWithTextureFilename(const std::string& filename, const Rect& rect);
bool initWithTexture(Texture2D* pobTexture, const Rect& rect, bool rotated, const Vec2& offset, const Size& originalSize)
通过精灵帧获取设置纹理
Texture2D* getTexture(void);
void setTexture(Texture2D* pobTexture); 相关属性
Vec2 _offset;//偏移量
Size _originalSize;原始大小
Rect _rectInPixels;//像素RECT
bool _rotated;//是否旋转
Rect _rect;
Vec2 _offsetInPixels;//像素单位偏移量
Size _originalSizeInPixels;
Texture2D *_texture;//持有的纹理指针
std::string _textureFilename;//纹理名字 --- <a id='SpriteFrameCache' name='SpriteFrameCache'> </a> ####[SpriteFrameCache](#top). ---
SpriteFrameCache也就是精灵帧缓存,其主要的工作流程是读取一张大的纹理图片,同时通过plist文件,将这张大图片上的小图片的相关信息,也就是截取小图片的纹理信息保存在精灵帧中。
Map<std::string, SpriteFrame*> _spriteFrames;//保存精灵帧的啊
ValueMap _spriteFramesAliases;//format值为3, Zwoptex导出的格式,在format为3的情况下,保存key为aliases的精灵帧,在format其他情况下没有赋值该值
std::set<std::string>* _loadedFileNames;//大图片的名字,也就是plist的名字
获取单例的SpriteFrameCache对象。sharedSpriteFrameCache方法已经弃用
static SpriteFrameCache* getInstance(void);
销毁SpriteFrameCache对象,purgeSharedSpriteFrameCache已经弃用
void SpriteFrameCache::destroyInstance()
{
CC_SAFE_RELEASE_NULL(_sharedSpriteFrameCache);
}
初始化_spriteFrames等属性,在getInstance中回调init方法
bool SpriteFrameCache::init(void)
{
_spriteFrames.reserve(20);//设置所需要保存的帧的数量,默认数量为20
_spriteFramesAliases.reserve(20);//设置所需要保存aliases的精灵帧的数量,默认为20
_loadedFileNames = new std::set<std::string>();
return true;
}
_spriteFrames是cocos2d::Map<K,V>类型,cocos2d::Map<K,V>是使用std::unordered_map作为底层结构的关联式容器。而std::unordered_map是一个存储键值对的关联式容器,它可以通过它们的键快速检索对应的值。使用unordered_map,键通常是唯一的,而值则与这个键对应。
在unordered_map内部,元素是无序,它们是根据键的哈希值来存取的,存取的时间复杂度是常量,超级快。(参考自:http://www.cocoachina.com/bbs/read.php?tid=202255)
将精灵帧添加到_spriteFrames中
void addSpriteFramesWithFile(const std::string& plist);//自动创建的纹理Texture2D
void addSpriteFramesWithFile(const std::string& plist, const std::string& textureFileName);//需要传入纹理名字,通过名字创建纹理
void addSpriteFramesWithFile(const std::string&plist, Texture2D *texture);//直接传入一个纹理 以上三个方法其中2,3方法类似,都是直接创建Texture2D,调用addSpriteFramesWithDictionary方法,关于方法1,则需要遍历plist文件,获取纹理名字,来创建纹理。下面就来看看第一个方法的具体实现:
void SpriteFrameCache::addSpriteFramesWithFile(const std::string& pszPlist)
{
if (_loadedFileNames->find(pszPlist) == _loadedFileNames->end())//通过查找_loadedFileNames来判断是否该plist已经将精灵帧添加到精灵帧缓存中
{
std::string fullPath = FileUtils::getInstance()->fullPathForFilename(pszPlist);//获取该plist的完整路径
ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(fullPath);//将该plist文件转换成ValueMap
string texturePath("");//初始化纹理名字路径
if (dict.find("metadata") != dict.end())//在dict中查找是否含有metadata的key
{
ValueMap& metadataDict = dict["metadata"].asValueMap();//将metadata的value转换成ValueMap类型
texturePath = metadataDict["textureFileName"].asString();//查找metadataDict中是否含有textureFileName这个key,如果有把她的value转换成string类型
}
if (!texturePath.empty())//判断路径是否为空
{
texturePath = FileUtils::getInstance()->fullPathFromRelativeFile(texturePath.c_str(), pszPlist);//获取纹理其完整路径
}
else
{
texturePath = pszPlist;//如果路径为空,则直接把plist的路径赋值给他
size_t startPos = texturePath.find_last_of("."); //找到其后缀名.的位置
texturePath = texturePath.erase(startPos);//抹除.后面的全部内容
texturePath = texturePath.append(".png");//添加上.png,这里要看清是png类型,所以如果没有加密的plist文件,需要确保纹理图片是png,且名字要一样
}
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(texturePath.c_str());//终于找到这一步,和方法2,3一样,将纹理图片名字传给纹理缓存,创建纹理
if (texture)
{
addSpriteFramesWithDictionary(dict, texture);//这方法是干货,将plist的内容,以及纹理Texture2D传过去,为啥要Texture2D呢?因为精灵帧需要啊.真正的处理plist文件,保存精灵帧的地方
_loadedFileNames->insert(pszPlist);//将该纹理名字插入_loadedFileNames中,方便前面的判断
}
else
{
CCLOG("cocos2d: SpriteFrameCache: Couldn't load texture");
}
}
}
在上面我们看到addSpriteFramesWithFile三个方法都调用了addSpriteFramesWithDictionary这方法,可见他的重要性,在看这代码之前我们得先了解下plist文件,看完了也得看代码了:
void SpriteFrameCache::addSpriteFramesWithDictionary(ValueMap& dictionary, Texture2D* texture)
{
//typedef std::unordered_map<std::string, Value> ValueMap
ValueMap& framesDict = dictionary["frames"].asValueMap();//获取key为frames的values
int format = 0;//plist的格式类型
if (dictionary.find("metadata") != dictionary.end())//查找key为metadata
{
ValueMap& metadataDict = dictionary["metadata"].asValueMap();
format = metadataDict["format"].asInt();//获取format的类型
}
for (auto iter = framesDict.begin(); iter != framesDict.end(); ++iter)//迭代framesDict
{
ValueMap& frameDict = iter->second.asValueMap();//获取value值
std::string spriteFrameName = iter->first;//获取key值,也就是图片帧的名字
SpriteFrame* spriteFrame = _spriteFrames.at(spriteFrameName);//查找在_spriteFrames是否有记录相同名字的图片
if (spriteFrame)//在_spriteFrames中已经含有spriteFrameName的帧,则调过继续
{
continue;
}
if(format == 0)
{
。//格式为0,Flash版本的具体逻辑省虑。。
}
else if(format == 1 || format == 2) //TexturePacker的主要文件格式
{
Rect frame = RectFromString(frameDict["frame"].asString());//将String类型转换成Rect类型
bool rotated = false;
if (format == 2)
{
rotated = frameDict["rotated"].asBool();
}
Vec2 offset = PointFromString(frameDict["offset"].asString());//将String类型转换成Vec2
Size sourceSize = SizeFromString(frameDict["sourceSize"].asString());//将string类型转换成Size
spriteFrame = new SpriteFrame();//创建一个精灵帧
spriteFrame->initWithTexture(texture,
frame,
rotated,
offset,
sourceSize
);//初始化一个精灵帧,将一系列参数传入
}
else if (format == 3)//Zwoptes1.0.2以后的主要文件格式
{
Size spriteSize = SizeFromString(frameDict["spriteSize"].asString());
Vec2 spriteOffset = PointFromString(frameDict["spriteOffset"].asString());
Size spriteSourceSize = SizeFromString(frameDict["spriteSourceSize"].asString());
Rect textureRect = RectFromString(frameDict["textureRect"].asString());
bool textureRotated = frameDict["textureRotated"].asBool();
ValueVector& aliases = frameDict["aliases"].asValueVector();//获取aliases的值,返回的是一个数组,具体查看下文plist-3文档
for(const auto &value : aliases) {
std::string oneAlias = value.asString();
if (_spriteFramesAliases.find(oneAlias) != _spriteFramesAliases.end())
{
}
_spriteFramesAliases[oneAlias] = Value(spriteFrameName);//将key为oneAlias,value为Value(spriteFrameName)赋值到_spriteFramesAliases
}
spriteFrame = new SpriteFrame();
spriteFrame->initWithTexture(texture,
Rect(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height),
textureRotated,
spriteOffset,
spriteSourceSize);
}
// add sprite frame
_spriteFrames.insert(spriteFrameName, spriteFrame);//将图片纹理名字,已经精灵帧插入到_spriteFrames中
spriteFrame->release();
}
除了通过plist文件插入精灵帧外,还可以直接将精灵帧插入
void addSpriteFrame(SpriteFrame *frame, const std::string& frameName);//通过传入精灵帧,以及纹理名字。如果在_spriteFrames已经传在frameName的精灵帧,那原先的精灵帧将被覆盖
有添加插入精灵帧,当然也就有移除精灵帧,我们在插入的过程中其实我们知道,精灵帧主要是插入到_spriteFrames,移除当然也就是移除_spriteFrames里的东西,当然在此过程中也就需要移除_loadedFileNames。否则我下次通过plist插入不进来精灵帧啊,当然在次过程中不能忘了_spriteFramesAliases,这仅仅只在format == 3的时候保存了纹理name.下面大体的看下移除的方法:
void removeSpriteFrames();//移除全部的精灵帧,只有把上诉三个属性全部clear就可以
void removeUnusedSpriteFrames();//移除没有使用的精灵帧,什么叫没有用的精灵帧呢??SpriteFrame的引用计数为1,那就是没有使用啊。这样就可以通过_spriteFrames.erase(toRemoveFrames)移除没有使用的了。
void removeSpriteFrameByName(const std::string& name);//根据名字移除,那直接下面两个方法不就可以_spriteFrames.erase(key)。_spriteFramesAliases.erase(key);具体的还是见代码好啊
void removeSpriteFramesFromTexture(Texture2D* texture);//通过迭代比较精灵帧的纹理是否和texture相等,来移除对应的纹理精灵
void removeSpriteFramesFromFile(const std::string& plist);//根据plist文件移除精灵,好好想下,移除的步骤其实,就是得知道纹理图片的名字,所以肯定是得通过ValueMap把plist的纹理图片找出来,移除图片,所以当前方法会用到下面的方法
void removeSpriteFramesFromDictionary(ValueMap& dictionary);//这方法也就是在上面方法将的,通过ValueMap把图片名字取出来,移除啊
移除精灵帧主要通过两种途径,要不就是根据纹理名字erase。要不就是通过比较精灵帧中的纹理是否和我将要移除的纹理一样来erase。
根据名字从精灵帧缓冲中获取精灵帧,当然也就很轻松了啊。。。
SpriteFrame* getSpriteFrameByName(const std::string& name);
####SpriteFrameCache vs. TextureCache
—
以下摘抄自:http://www.cocoachina.com/bbs/read.php?tid=200359
SpriteFrameCache精灵框帧缓存。顾名思义,这里缓存的是精灵帧SpriteFrame,它主要服务于多张碎图合并出来的纹理图片。这种纹理在一张大图中包含了多张小图,直接通过TextureCache引用会有诸多不便,因而衍生出来精灵框帧的处理方式,即把截取好的纹理信息保存在一个精灵框帧内,精灵通过切换不同的框帧来显示出不同的图案。
跟TextureCache功能一样,将SpriteFrame缓存起来,在下次使用的时候直接去取。不过跟TextureCache不同的是,如果内存池中不存在要查找的图片,它会提示找不到,而不会去本地加载图片。
* TextureCache时最底层也是最有效的纹理缓存,缓存的是加载到内存中的纹理资源,也就是图片资源。
* SpriteFrameCache精灵框帧缓存,缓存的时精灵帧。
* SpriteFrameCache是基于TextureCache上的封装。缓存的是精灵帧,是纹理指定区域的矩形块。各精灵帧都在同一纹理中,通过切换不同的框帧来显示出不同的图案。
####plist文件格式 — 以下摘抄自:http://zengrong.net/post/1981.htm
-
什么是plist文件格式?
这是一种人类可读的串行化对象文件,由苹果公司发明,最早用于NeXTSTEP系统。详情看这里:Plist(http://zh.wikipedia.org/wiki/Plist) 。cocos2d-x 从 cocos2d-iphone 发展而来,因此在引擎中大量使用了这种文件格式。
-
cocos2d-x在哪些地方使用了plist格式?
*.图像纹理定义文件:将多个纹理拼在一张大图上,使用 CCSpriteFrameCache 可以载入这类plist文件; *.Label纹理定义文件:作用与图像纹理定义文件类似,只不过处理的是自己,面向 CCLabelAtlas; *.帧动画定义:定义一个或多个动画中,使用哪些纹理,使用 CCAnimationCache 可以载入这类plist文件;
-
图像纹理定义文件格式说明
cocos2d-x中的纹理定义格式,是以Zwoptex生成的格式为标准的。 Zwoptex生成的格式,有4种主要不同的版本: *.format值为0,代表Flash版本; *.format值为1,Zwoptex 0.4b以前支持; *.format值为2,Zwoptex 1.0以后支持,与format1的区别在于支持旋转; *.format值为3,属性名称进行了大幅修改,Zwoptes1.0.2之后支持。 这3种格式的plist文件,cocos2d-x都能支持,具体的解析代码在CCSpriteFrameCache::addSpriteFramesWithDictionary。 TexturePacker生成的for cocos2d plist格式与Zwoptex生成的format为2的格式相同。
-
format为0的plist文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>texture</key> <dict> <key>width</key> <integer>256</integer> <key>height</key> <integer>128</integer> </dict> <key>frames</key> <dict> <key>grossini.png</key> <dict> <key>x</key> <integer>103</integer> <key>y</key> <integer>1</integer> <key>width</key> <integer>51</integer> <key>height</key> <integer>109</integer> <key>offsetX</key> <real>0</real> <key>offsetY</key> <real>-1</real> <key>originalWidth</key> <integer>85</integer> <key>originalHeight</key> <integer>121</integer> </dict> </dict> </dict> </plist>
-
format为2的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>parts-dragon_leg_l.png</key> <dict> <key>frame</key> <string>{ {866,2},{152,254} }</string> <key>offset</key> <string>{0,0}</string> <key>rotated</key> <false/> <key>sourceColorRect</key> <string>{ {0,0},{152,254} }</string> <key>sourceSize</key> <string>{152,254}</string> </dict> </dict> <key>metadata</key> <dict> <key>format</key> <integer>2</integer> <key>realTextureFileName</key> <string>dragon.png</string> <key>size</key> <string>{1024,2048}</string> <key>smartupdate</key> <string>$TexturePacker:SmartUpdate:26d1d28da42d49170ab3142654fea750:1/1$</string> <key>textureFileName</key> <string>dragon.png</string> </dict> </dict> </plist>
-
format为3的plist文件
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>frames</key>
<dict>
<key>armyLevelBg.png</key>
<dict>
<key>aliases</key>
<array/>
<key>spriteColorRect</key>
<string>{ {0, 0}, {61, 36} }</string>
<key>spriteOffset</key>
<string>{0, -0}</string>
<key>spriteSize</key>
<string>{61, 36}</string>
<key>spriteSourceSize</key>
<string>{61, 36}</string>
<key>spriteTrimmed</key>
<true/>
<key>textureRect</key>
<string>{ {195, 2}, {36, 61} }</string>
<key>textureRotated</key>
<true/>
</dict>
</dict>
<key>metadata</key>
<dict>
<key>version</key>
<string>1.6.0</string>
<key>format</key>
<integer>3</integer>
<key>size</key>
<string>{256, 512}</string>
<key>name</key>
<string>army_zw.zwd</string>
<key>textureFileName</key>
<string>army_zw_cocos2d.png</string>
<key>premultipliedAlpha</key>
<false/>
</dict>
</dict>
</plist>
####COCOS2D-X常用宏. —
#define CC_SWAP(x, y)//交互x,y的值
#define CCRANDOM_MINUS1_1()//生成一个 -1 到 1的随机数
#define CCRANDOM_0_1() // 生成一个 0 到 1的随机数
#define CC_DEGREES_TO_RADIANS(__ANGLE__) // 角度转弧度
#define CC_RADIANS_TO_DEGREES(__ANGLE__) //弧度转角度
#define kRepeatForever //定时器永远执行
#define CC_BLEND_SRC //GL_ONE表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算
#define CC_BLEND_DST //GL_ONE_MINUS_SRC_ALPHA表示用1.0减去源颜色的alpha值来作为因子
#define CC_NODE_DRAW_SETUP() //getGLProgram()->use(); getGLProgram()->setUniformsForBuiltins(_modelViewTransform);设置gl的状态,以及设置模型视图矩阵以及投影矩阵
#define CC_DIRECTOR_END()/// 停止 director 并从内存中移除, 从父级移除 CCGLView
#define FLT_EPSILON //趋0最小float
#define DISALLOW_COPY_AND_ASSIGN(TypeName) //用来限制类型复制、拷贝和默认的一些转换操作等对class类型变量的误操作,也可以提高性能,减少编译器自作主张为我们生成一些无法预料的代码
#define CC_CONTENT_SCALE_FACTOR()//缩放因子
#define CC_RECT_PIXELS_TO_POINTS(__rect_in_pixels__) //RECT的转换,主要是除以CC_CONTENT_SCALE_FACTOR()
#define CC_RECT_POINTS_TO_PIXELS(__rect_in_points_points__)//RECT的转换 主要通过*CC_CONTENT_SCALE_FACTOR()
#define CC_POINT_PIXELS_TO_POINTS(__pixels__)//点的转换 主要是除以CC_CONTENT_SCALE_FACTOR()
#define CC_POINT_POINTS_TO_PIXELS(__points__)//点的转换 主要通过*CC_CONTENT_SCALE_FACTOR()
#define CC_SIZE_PIXELS_TO_POINTS(__size_in_pixels__)//SIZE的转换
#define CC_SIZE_POINTS_TO_PIXELS(__size_in_points__)
#define CHECK_GL_ERROR_DEBUG()//检测opengl的错误
#define CC_INCREMENT_GL_DRAWS(__n__)//batches绘制次数
#define CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(__drawcalls__, __vertices__)//batches绘制次数,以及vertices绘制个数
/**
以下CC_CALLBACK_* 都是std::bind的宏定义,由于std::bind的占位符的数量不一样,也就形成了CC_CALLBACK_0,CC_CALLBACK_1等形式。
__target__表示执行这个回调函数的具体对象;##__VA_ARGS__是可变参数宏,std::bind是在C++ 11里新加入的成员。可以将bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。调用bind的一般形式为: auto newCallback = bind(callback,arg_list); 其中,newCallback是一个可调用对象,arg_list是可以用逗号分隔的参数列表,至于是啥参数,那就看callback函数里有啥参数啦。也就是说,当我们调用newCallback时,newCallback会调用函数callback,并传递参数arg_list给callback.
有一个函数callback,如:int callback(int one,char two,double three);
下面我们用bind来调用callback:
auto newCallback = bind(callback,_1,_2,1.5);
int x = newCallback(10,'h'); //这句相当于:int x = callback(10,'h',1.5);
“_1″是一个占位符对象,用于表示当函数callback通过函数newCallback进行调用时,函数newCallback的第一个参数在函数callback的参数列表中的位置。第一个参数称为”_1″, 第二个参数为”_2″,依此类推,有意思吧。至于‘1.5’是指默认参数,它处于_1和_2的后面,所以它就是double类型的参数了.
*/
#define CC_CALLBACK_0(__selector__,__target__, ...)//std::bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) //std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) //std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) //std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)
####C相关方法学习. —
+. void *memcpy(void *dest, const void *src, size_t n)
:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中.
+. strcpy和memcpy主要有以下3方面的区别:
* 复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
* 复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度
* 用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
+. erase函数的原型如下:
* string& erase ( size_t pos = 0, size_t n = npos );
* iterator erase ( iterator position );
* iterator erase ( iterator first, iterator last );
+. 也就是说erase有三种用法:
* erase(pos,n); 删除从pos开始的n个字符,比如erase(0,1)就是删除第一个字符
* erase(position);删除position处的一个字符(position是个string类型的迭代器)
* erase(first,last);删除从first到last之间的字符(first和last都是迭代器)
青春是一场大雨,即使感冒了,还盼回头再淋一次......微笑永远是一个人身上最好看的东西...