学习cocos2dx就要了解它最基础的东西,那就是纹理和精灵。
1.CCTexture2D
为什么先讲CCTexture2D呢,因为我觉得这个是cocos2dx最基础的一个对象,就是纹理贴图,因为我们看到的所有东西都是由一张张纹理构成的。
CCTexture2D是贴图纹理,是缓存到GPU中的图片数据。它是cocos2d-x渲染图形的重要参数,用来贴图,因为cocos2d-x使用opengl es绘制2d图形的,它的尺寸是2的n次方。
CCTexture2D* cache = CCTextureCache::sharedTextureCache()->addImage("hero.png");
2.CCSprite
CCSprite是显示到场景中的精灵,是被实际画出来的东西,它是CCNode的子类,它的内部封装了CCTexture2D(纹理)。
CCSprite的创建:
一般我们通过CCSprite提供的工厂方法来创建CCSprite对象,而CCSprite提供了不止一个这样的方法,我们分别来看:
1.最简单的create(),该方法不带任何参数,通过它可以返回一个不带纹理的空CCSprite对象,当然创建成功之后你可以调用CCSprite对象的初始化方法进行初始化。
CCSprite * pSprite = CCSprite::create();
2.通过指定图片文件名来创建create(const char * pszFileName),该方法的参数是图片文件的资源位置。
CCSprite * pSprite = CCSprite::create("test.png");
3.通过指定图片文件名并给出图片显示区域create (const char *pszFileName,const CCRect &rect),第二个参数是CCRect对象的引用。
CCRect rect; rect.setRect(0,100,100); CCSpriteFrame * pFrame = CCSpriteFrame::create("test.jpg",rect); CCSprite * pSprite3 = CCSprite::createWithSpriteFrame(pFrame);
4.通过指定CCTexture2D材质对象来创建createWithTexture (CCTexture2D *pTexture),这里的参数类型是一个我们之前没见过的类。我们知道Cocos2d是基于显卡编程的,因此在创建显示对象时,我们实际上是通过纹理贴图来让它们拥有丰富的显示内容的。实际上纹理贴图才是CCSprite对象的内部显示原理,而之前的两个方法只是将纹理对象的创建封装到了内部。
为了创建纹理,我们首先需要一个CCImage对象,具体见下:
CCImage * image = new CCImage(); image->initWithImageFile("test.jpg",CCImage::kFmtJpg);//指定图片路径和图片格式 CCTexture2D * tex = new CCTexture2D(); tex->initWithImage(image);
这样一个纹理便可用了:
CCSprite * pSprite2 = CCSprite::createWithTexture(tex);
5.通过上面的方法创建出来的CCSprite对象的显示区域为整个纹理,通过createWithTexture (CCTexture2D *pTexture,const CCRect &rect)方法可以给CCSprite对象指定显示区域:
CCRect rect; rect.setRect(0,100); CCSprite * pSprite2 = CCSprite::createWithTexture(tex,rect);
6.最后我们来看通过CCSpriteFrame对象创建CCSprite的两个方法:createWithSpriteFrame (CCSpriteFrame *pSpriteFrame)和createWithSpriteFrameName (const char *pszSpriteFrameName),其实主要是前一个方法。
这里的关键是CCSpriteFrame是什么?CCSpriteFrame拥有一个纹理对象(CCTexture2D)和一个矩形对象(CCRect),我们可以将它理解为CCSprite和CCTexture2D的中间者,CCSprite通过CCSpriteFrame来了解需要显示的内容以及显示的区域。具体地:
CCRect rect; rect.setRect(0,rect); CCSprite * pSprite3 = CCSprite::createWithSpriteFrame(pFrame);
3.CCSpriteFrame CCSpriteFrame是相对于动画来说的概念,代表动画中“帧”,CCSpriteFrame 是贴图纹理CCTexture2D中某块区域,或者这个贴图纹理。
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);
所以CCSpriteFrame都是他用过Texture2D来初始化的,然后再创建Sprite供我们用。
比如我们要利用打包的plist图集的一张图片来创建精灵。
就像上面的,我们那就需要使用Sprite:createWithSpriteFrameName(‘’)来创建或者先创建一个SpriteFrame来创建Sprite了。
CCSprite也可以用来定义动画层的每一帧,定义好厚以CCAction的形式作用到一个CCSprite上来呈现动画效果
CCTexture2D和CCSpriteFrame都是我们看不到的,我们真正能看到的应该是CCSprite,也就是最终呈现在游戏界面内的。
4.CCSpriteBatchNode
是用于提高精灵渲染速度的技术。它可以提高渲染大量相同精灵的速度,不过它同纹理贴图集配合使的效率最高。如果你将纹理贴图集与精灵批处理配合使用的话,你只要调用一次渲染方法就可以完成纹理贴图集里所有图片的渲染。
“渲染方法调用”(draw call)是把必要的信息传递给图形处理硬件以完成整个或者部份图片渲染的过程。当你使用CCSprite的时候,每生成一个CCSprite节点都会调用一次渲染方法。每一次渲染方法调用导致的系统开销叠加起来会使游戏的帧率大约降低15%或者更多(除非你使用CCSpriteBatchNode,它的作用是作为一个额外的层用于添加多个精灵节点。前提是这些精灵节点使用的是同一个纹理贴图)。
CCSpriteBatchNode
每次系统在屏幕上渲染一张贴图时,图形处理硬件必须首先准备渲染,然后渲染图形,最后在完成渲染以后进行清理。上述过程是每一次在启动渲染和结束渲染之间存在的固定系统开销。如果图形处理硬件知道你需要使用同一张纹理贴图渲染一组精灵的话,图形处理硬件将只需要为这一组精灵执行一次准备,渲染,最后清理的过程了。
你可以看到屏幕上有几百个相同的子弹。如果你逐个渲染它们的话,你的游戏帧率将会下降至少15%。如果你使用CCSpriteBatchNode,你就可以避免帧率的下降。
把使用同一张纹理贴图的一组CCSprite节点添加到同一个CCSpriteBatchNode里,比逐个渲染CCSprite要高效很多。
以下是生成CCSprite的常用代码:
CCSprite *sprite=CCSprite::create("sprite.png"); this->addChild(sprite);
当然,向CCSpriteBatchNode添加一个CCSprite节点不会有任何效率上的提高。
接下来,我把许多使用同一张纹理贴图的精灵节点添加到了同一个CCSpriteBatchNode里
CCSpriteBatchNode *node=CCSpriteBatchNode::create("s1.png"); this->addChild(node); for(int i=0;i<100;i++){ CCSprite *sprite=CCSprite::create("s1.png"); node->addChild(sprite); }
CCSpriteBatchNode的作用很像CCLayer,因为它本身并不显示在屏幕上。不过你只能把CCSprite加入CCSpriteBatchNode。CSpriteBatchNode将一个图片文件名作为参数,使用这个参数的原因是所有被添加进CCSpriteBatchNode的CCSprite节点都必须使用同一个图片文件。如果你没有在CCSprite中使用相同的图片,你将会在调试窗口中得到报错信息。
什么时候应该使用CCSpriteBatchNode?
当你需要显示两个或者更多个相同的CCSprite节点时,你可以使用CCSpriteBatchNode。组合在一起的CCSprite节点越多,使用CCSpriteBatchNode得到的效果提升就越大。
不过也有一些限制。因为所有的CCSprite节点都添加到同一个CCSpriteBatchNode中,所以所有CCSprite节点都会使用相同的z-order(深度)来渲染.
另一个缺点是所有添加到同一个CCSpriteBatchNode中的CCSprite节点必须使用同一个纹理贴图。不过那也意味着CCSpriteBatchNode非常适合使用纹理贴图集。使用纹理贴图集的时候,你不仅仅局限于渲染一张图片;你可以把不同的图片放到同一个纹理贴图集中,然后利用CCSpriteBatchNode将所有图片渲染出来,以提高渲染速度。
5.CCTextureCache
CCTextureCache贴图纹理缓存类,这个类可以把一些图片加载到GPU之后,缓存起来,以后直接取就可以了。它相当于CCTexture2D的容器,是内存池,用来缓存CCTexture2D对象的,它内部有一个字典CCMutableDictionary m_pTextures,key为图片的名称,值是CCTexture2D。当调用它的addImage函数添加图片时,会先根据图片名称去内存中查找是否已存在,是则直接取出返回。下面是addImage部分源码:
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(); bool bRet = image->initWithImageFile(fullpath); CC_BREAK_IF(!bRet); texture = new (std::nothrow) Texture2D(); if( texture && texture->initWithImage(image) ) { // texture already retained,no need to re-retain it _textures.insert( std::make_pair(fullpath,texture) ); } } while (0); } return texture; }
6.CCSpriteFrameCache
CCSpriteFrameCache精灵帧缓存类,它是管理CCSpriteFrame的内存池,跟CCTextureCache功能一样,不过跟CCTextureCache不同的是,如果内存池中不存在要查找的帧,它会提示找不到,而不会去本地加载图片。它的内部封装了一个字典:CCDictionary *m_pSpriteFrames,key为帧的名称。通过.plist和.png图片来缓存了一些CCSpriteFrame的对象,这个类解决碎图问题,大大提高了效率,通过一些工具把一些碎图(小图片)整合到一张大图片中,通过加载一次贴图纹理,就把所有的整合到大图中的图片加载了进去,缓存到CCSpriteFrameCache 对象中,以后就可以通过碎图的名字,从CCSpriteFrameCache 对象中获取精灵帧对象。 然后可以使用获取的精灵帧做其他处理。