1.大量绘制精灵
bool HelloWorld::init(){ if(!Layer::init()){ return false; } //创建一堆的精灵 for(int i=0;i<20000;i++){ Sprite* s = Sprite::create("sprite.png"); s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300)); this->addChild(s); } }查看调试信息
- 第一行的数字表示顶点数量;
- 第二行的数字表示渲染批次,意思就是,要执行多少次渲染;
- 第三行左边的数字是帧率,右边的是每一帧所需的时间;
在cocos2d-x 2.0的时候,像这样创建这么多精灵的话应该已经卡掉了吧;
然后在3.0,cocos2d-x自己做了优化,会自动处理这样的情况,叫做Auto-batching;
2.3.0的新功能Auto-batching
要Auto-batching生效,需要满足的条件(好吧,看不懂啊)
一般情况下,我们用同一张图片,并没有做特殊处理,就能满足Auto-batching的条件,不需要我们去做处理
3.Auto-batching的一些问题
bool HelloWorld::init(){ if(!Layer::init()){ return false; } //创建一堆的精灵 for(int i=0;i<20000;i++){ Sprite* s = Sprite::create("sprite.png"); s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300)); this->addChild(s); s = Sprite::create("sprite1.png"); s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300)); this->addChild(s); } }循环创建了两个不同的精灵,运行的结果和预期的不同,Auto-batching没有生效
Auto-batching的另外一个限制条件,对于不连续的渲染对象,是无法在一个渲染批次里进行渲染的。
怎样才算是“连续的对象”,最简单的解释就是:
- 如果节点具有相同的globalZOrder值,则是连续的;
- 否则,如果节点具有相同的localZOrder值,则是连续的;
- 否则,如果节点具有相同的orderOfArrival值,则是连续的;
- 连续的节点还必须使用相同的纹理,就是相同的图片咯。
bool HelloWorld::init(){ if(!Layer::init()){ return false; } //创建一堆的精灵 for(int i=0;i<20000;i++){ Sprite* s = Sprite::create("sprite.png"); s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300)); this->addChild(s); s->setGlobalZOrder(1); s = Sprite::create("sprite1.png"); s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300)); this->addChild(s); s->setGlobalZOrder(2); } }
给每个精灵设置了globalZOrder属性,globalZOrder对排序有了影响,,使得所有的sprite精灵都是连续的,sprite1也是连续的
这就满足了Auto-batching的条件了。
影响精灵排序的优先级:globalZOrder优先于LocalZOrder,localZOrder优先于orderOfArrival
4.深入了解Auto-batching,嗯,我还看不懂,之后再回过来看
5.一次渲染,SpriteBatchNode的特别之处
SpriteBatchNode如果包含了子节点,那么所有的子节点在绘制的时候只会调用一次OpenGL的渲染。只有使用同一纹理的精灵才能添加到SpriteBatchNode上,如果精灵没有添加到SpriteBatchNode上,那么每一个精灵的绘制都会调用一次OpenGL的渲染(当然咯,满足Auto-batching条件的除外咯)。
bool HelloWorld::init(){ if(!Layer::init()){ return false; } //创建批次渲染对象,并添加到场景中 SpriteBatchNode* batchNode = SpriteBatchNode::create("sprite.png"); this->addChild(batchNode); //创建精灵,加入到批次渲染对象中 for(int i=0;i<999;i++){ Sprite* s = Sprite::cretae("sprite.png"); s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*200)); batchNode->addChild(s); } return true; }这边是先将SpriteBatchNode加入到场景中,再将精灵加到SpriteBatchNode中;
当然,SpriteBatchNode也是有限制的,并需把精灵添加到SpriteBatchNode上,并且只允许添加精灵对象,其他的对象是不允许添加的。
6.Texture简单介绍---纹理
bool HelloWorld::init(){ <span style="white-space:pre"> </span>if(!Layer::init()){ <span style="white-space:pre"> </span>return false; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>//截取图片的一部分来创建精灵 <span style="white-space:pre"> </span>Sprite* s1 = Sprite::createWithSpriteFrame(SpriteFrame::create("sprite.png",Rect(0,60,50))); <span style="white-space:pre"> </span>Spritr* s2 = Sprite::create("sprite.png"); <span style="white-space:pre"> </span>s1->setPosition(Point(100,200)); <span style="white-space:pre"> </span>s2->setPosition(Point(300,200)); <span style="white-space:pre"> </span>this->addChild(s1); <span style="white-space:pre"> </span>this->addChild(s2); <span style="white-space:pre"> </span>//获取两个精灵的纹理对象,通过控制台观察,发现两个精灵对象的值是一样的 <span style="white-space:pre"> </span>Texture2D* t1 = s1->getTexture(); <span style="white-space:pre"> </span>Texture2d* t2 = s2->getTexture(); <span style="white-space:pre"> </span> <span style="white-space:pre"> </span>return true; }
7.TexturePacker工具
当需创建多个精灵的时候,图片不一样,纹理就没有办法相同的,那就不能使用Auto-batching或者SpriteBatchNode了。
当一张图片中拥有多个精灵时。
bool HelloWorld::init(){ if(!Layer::init()){ return false; } //截取图片的一部分来创建精灵 Sprite* s1 = Sprite::createWithSpriteFrame("sprites.png",50));//精灵一 Spritr* s2 = Sprite::createWithSpriteFrame("sprites.png",Rect(30,30,50));//精灵二 s1->setPosition(Point(100,200)); s2->setPosition(Point(300,200)); this->addChild(s1); this->addChild(s2); return true; }以上是手动计算精灵在图中的位置,不过相对来说是比较麻烦的
接下来,我们就要使用到工具了
TexturePacker图片打包工具:http://www.codeandweb.com/texturepacker
不过打开来相对是比较慢的,还有,好像官网是不允许下载旧版本的,要输入什么东西
TexturePacker一些选项的作用
- Data Format:生成配置文件的格式类型,不同引擎支持不同的格式
- Texture Format:生成的图片的格式
- Image Format:图片色彩模式,木头使用的是RGBA4444
- Dithering:抖动显示,如果我们选择的图片色彩模式比较低(颜色数较少)导致图片失真严重,可以通过抖动显示来缓解失真程度
- Size constraints:图片打包都是按2的次方的
- Allow rotation:是否允许图片旋转,旋转在某些时候可以缩小图片
- Trim:是否允许把图片透明部分去掉
点击“Publish”按钮,导出打包后的文件"*.plist"和"*.png"
8.加载打包后的图片
既然用TexturePacker打包好了图片,我们当然就要使用它了
bool HelloWorld::init(){ if(!Layer::init()){ return false; } //将图片添加到精灵缓存池 SpriteFrameCache* frameCache = SpriteFrameCache::getInstance(); frameCache->addSpriteFramesWithFile("sprites.plist","sprites.png"); //用小图片的名字即可创建精灵,即使是旋转了的图片,这样创建出来也是正常的 Sprite* s = Sprite::createWithSpriteName("sprite1.png"); s->setPosition(Point(100,200)); This->addChild(s); return true; }
创建精灵时使用的是打包前图片的名字,plist文件里记录了各个图片在大图片中的位置和大小
9.动画Animation
Animate* HelloWorld::createAnimate1(){ //将15张动作的图片放到resources文件夹中 int sNum = 15;//动作图片的数量 SpriteFrame* frame = NULL;//每个图片为一个SpriteFrame对象 Vector<SpriteFrame*> frameVec; //用一个列表保存所有SpriteFrame对象 for(int i=1;i<=sNum;i++){ frame = SpriteFrame::create(StringUtils::format("run%d.png",i),130,130)); frameVec.pushBack(frame); } //使用SpriteFrame列表创建动画对象 Animation* animation = Animation::createWithSpriteFrames(frameVec); animation->setLoops(-1);//循环次数,-1表示无限循环 animation->setDelayPerUnit(0.1f);//设置每一帧播放延迟 //将动画包装成一个动作 Animate* action = Animate::create(animation); return action; } bool HelloWorld::init(){ if(!Layer::init()){ return false; } Sprite* sprite = Sprite::create("run1.png"); sprite->setPosition(Point(200,200)); sprite->runAction(createAnimate1);//直接执行就可以了 }在没有打包图片的时候,需要指定图片的大小,接下来我们就用打包后的图片来创建动画
Animate* HelloWorld::createAnimate1(){ //加载图片帧到缓存池 SpriteFrameCache* frameCache = SpriteFrameCache::getInstance(); frameCache.addSpriteFramesWithFile("boys.plist","boys.png"); //将15张动作的图片放到resources文件夹中 int sNum = 15;//动作图片的数量 SpriteFrame* frame = NULL;//每个图片为一个SpriteFrame对象 Vector<SpriteFrame*> frameVec; //用一个列表保存所有SpriteFrame对象 for(int i=1;i<=sNum;i++){ frame = frameCache->getSpriteFrameByName(StringUtils::format("run%d.png",i)); frameVec.pushBack(frame); } //使用SpriteFrame列表创建动画对象 Animation* animation = Animation::createWithSpriteFrames(frameVec); animation->setLoops(-1);//循环次数,-1表示无限循环 animation->setDelayPerUnit(0.1f);//设置每一帧播放延迟 //将动画包装成一个动作 Animate* action = Animate::create(animation); return action; }
10.创建动画的辅助类
AnimationUtil
头文件
#ifndef _ANIMATION_UTIL_H_ #define _ANIMATION_UTIL_H_ #include "cocos2d.h" class AnimationUtil{ static cocos2d::Animation* createWithStringFrameName(const char* name,float delay,int iLoops); static cocos2d::Animation* createWithStringFrameNameAndNum(const char* name,int num,int iLoops); }; #endifcpp
#include "AnimationUtil.h" USING_NS_CC; //根据名字来创建 Animation* AnimationUtil::createWithStringFrameName(const char* name,int iLoops){ <span style="white-space:pre"> </span>SpriteFrameCache* frameCache = SpriteFrameCache::getInstance(); <span style="white-space:pre"> </span>Vector<SpriteFrame*> frameVec; <span style="white-space:pre"> </span>SpriteFrame* frame = NULL; <span style="white-space:pre"> </span>int index = 1; <span style="white-space:pre"> </span>do{ <span style="white-space:pre"> </span>frame = frameCache->getSpriteFrameByName(StringUtils::format("%s%d.png",name,index++)); <span style="white-space:pre"> </span>if(frame == NULL){ <span style="white-space:pre"> </span>break; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>frameVec.pushBack(frame); <span style="white-space:pre"> </span>}while(true); <span style="white-space:pre"> </span>Animation* animation = Animation::createWithSpriteFrames(frameVec); <span style="white-space:pre"> </span>animation->setLoops(iLoops); <span style="white-space:pre"> </span>animation->setRestoreOriginalFrame(true); <span style="white-space:pre"> </span>animation->setDelayPerUnit(delay); <span style="white-space:pre"> </span>return animation; } //根据名字和数量来创建 Animation* AnimationUtil::createWithStringFrameNameAndNum(const char* name,int iLoops){ <span style="white-space:pre"> </span>SpriteFrameCache* frameCache = SpriteFrameCache::getInstance(); <span style="white-space:pre"> </span>Vector<SpriteFrame*> frameVec; <span style="white-space:pre"> </span>SpriteFrame* frame = NULL; <span style="white-space:pre"> </span>int index = 1; <span style="white-space:pre"> </span>for(int i = 1;i<=num;i++){ <span style="white-space:pre"> </span>frame = frameCache->getSpriteFrameByName(StringUtils::format("%s%d.png",index++)); <span style="white-space:pre"> </span>if(frame == NULL){ <span style="white-space:pre"> </span>break; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>frameVec.pushBack(frame); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>Animation* animation = Animation::createWithSpriteFrames(frameVec); <span style="white-space:pre"> </span>animation->setLoops(iLoops); <span style="white-space:pre"> </span>animation->setRestoreOriginalFrame(true); <span style="white-space:pre"> </span>animation->setDelayPerUnit(delay); <span style="white-space:pre"> </span>return animation; }使用方法
SpriteFrameCache* frameCache = SpriteFrameCache::getInstance(); frameCache->addSpriteFramesWithFile("runs.plist","runs.png"); Animation* action = AnimationUtil::createWithStringFrameName("run",0.1f,-1); sprite->runAction(Animate::create(action));