废话
今天和王爷一起去传说中的车库咖啡见了一个创业团队,跟他们有了一个小时左右的交流时间,交流的详情就不说了,总之,让我和王爷多了一个很好的话题,在回家的路上,我们就这个话题进行了友好、亲切而又愉快的交流,受益匪浅。
不过,最糟心的是,跑这一趟我竟然感冒了,主要是天气热,出了很多汗,而在地铁通道里风太大,晾了汗了,纠结死我了,明天还要去见一个离得比较近的创业团队,我在犹豫到底还要不要去。
概述
今天随便说说在Cocos2D-X开发中,关于对CCSprite方面的优化,总得来说就是尽可能减少渲染次数,这样可以使游戏运行更有效率,能够空出更多的CPU时间做其它的事情,以增加游戏的流畅与体验。
正文
Cocos2D为图片资源批量加载提供了CCSpriteFrameCache
类,此类可以通过使用Sprite Sheet文件来减少图片加载次数,简单来说,就是把多个图片拼成一个大图片,之后通过坐标在大文件上取到相应的图片,这个技术被广泛使用在网页设计及游戏开发中。在这里我使用了一个叫做Zwoptex的软件自动生成大图片及相应的.plist
,软件的具体使用方法就不在这里细说了(另外,朋友介绍说Texture Packer是可以更好的做这件事的工具,改天尝试一下)。
另外,Cocos2D还提供了一个CCSpriteBatchNode
类,这个类主要作用是,当相同图片资源被重复使用时,通过这个类可以减少使相同的图片只渲染一次,具体做法看下面的代码。
PS: 代码基于上一篇BLOG,只对关键部分进行说明。
GameSceneDH.h1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #ifndef __StudyCocos2D__GameSceneDH__ #define __StudyCocos2D__GameSceneDH__
#include <iostream> #include "cocos2d.h"
using namespace cocos2d;
class GameSceneDH: public CCLayer { public: static CCScene *scene(); virtual bool init(); CREATE_FUNC(GameSceneDH); };
|
GameSceneDH.cpp1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
#include "GameSceneDH.h" CCScene * GameSceneDH::scene() { CCScene *scene = CCScene::create(); CCLayer *layer = GameSceneDH::create(); scene->addChild(layer); return scene; }
bool GameSceneDH::init() { if (!CCLayer::init()) { return false; } CCSize winSize = CCDirector::sharedDirector()->getWinSize(); CCSprite *background = CCSprite::create("大唐边境.jpg"); CCSize backgroundSize = background->getContentSize(); background->setScale(winSize.width / backgroundSize.width); background->setPosition(ccp(winSize.width/2, winSize.height/2)); this->addChild(background); CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache(); frameCache->addSpriteFramesWithFile("DH.plist"); CCSpriteFrame *frameDH0, *frameDH1, *frameDH2, *frameDH3, *frameDH4, *frameDH5; frameDH0 = frameCache->spriteFrameByName("DH (0).png"); frameDH1 = frameCache->spriteFrameByName("DH (1).png"); frameDH2 = frameCache->spriteFrameByName("DH (2).png"); frameDH3 = frameCache->spriteFrameByName("DH (3).png"); frameDH4 = frameCache->spriteFrameByName("DH (4).png"); frameDH5 = frameCache->spriteFrameByName("DH (5).png"); frameDH0->autorelease(); frameDH1->autorelease(); frameDH2->autorelease(); frameDH3->autorelease(); frameDH4->autorelease(); frameDH5->autorelease(); CCSprite *sp0 = CCSprite::createWithSpriteFrame(frameDH0); sp0->setPosition(ccp(50, 150)); this->addChild(sp0); CCSprite *sp1 = CCSprite::createWithSpriteFrame(frameDH1); sp1->setPosition(ccp(140, 150)); this->addChild(sp1); CCSprite *sp2 = CCSprite::createWithSpriteFrame(frameDH2); sp2->setPosition(ccp(230, 150)); this->addChild(sp2); CCSprite *sp3 = CCSprite::createWithSpriteFrame(frameDH3); sp3->setPosition(ccp(320, 150)); this->addChild(sp3); CCSprite *sp4 = CCSprite::createWithSpriteFrame(frameDH4); sp4->setPosition(ccp(410, 150)); this->addChild(sp4); CCSprite *sp5 = CCSprite::createWithSpriteFrame(frameDH5); sp5->setPosition(ccp(500, 150)); this->addChild(sp5); return true; }
|
以上代码中,第25-29行为游戏添加了背景,这个不在本文的写作目的之中,其中27、28行是将背景的大小和位置调整到铺满整个窗口。
第31、32行利用.plist
文件初始化了一个SpriteSheet图片,CCSpriteFrameCache
类全局有效以单例的形式使用,但是为了方便后面使用,这里单独声明了一个CCSpriteFrameCache
对象。
从34行开始到第40行,声明一组CCSpriteFrame
对象,并通过文件名在CCSpriteFrameCache
类中初始化。从49行到66行,通过已经创建的``CCSpriteFrame对象创建一些相应的
CCSprite`实例,并做位置做相应的调整后加入到场景中。
在这里为了便于说明,尽可能少的牵涉到无关内容,对代码本身并没做优化。
注意看界面左下角的帧率信息。
下面,我们在代码中通过CCSpriteBatchNode
对程序进行优化,代码如下:
GameSceneDH.cpp1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
#include "GameSceneDH.h" CCScene * GameSceneDH::scene() { CCScene *scene = CCScene::create(); CCLayer *layer = GameSceneDH::create(); scene->addChild(layer); return scene; }
bool GameSceneDH::init() { if (!CCLayer::init()) { return false; } CCSize winSize = CCDirector::sharedDirector()->getWinSize(); CCSprite *background = CCSprite::create("大唐边境.jpg"); CCSize backgroundSize = background->getContentSize(); background->setScale(winSize.width / backgroundSize.width); background->setPosition(ccp(winSize.width/2, winSize.height/2)); this->addChild(background); CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache(); frameCache->addSpriteFramesWithFile("DH.plist"); CCSpriteBatchNode *batchNode = CCSpriteBatchNode::create("DH.png"); this->addChild(batchNode); CCSpriteFrame *frameDH0, *frameDH1, *frameDH2, *frameDH3, *frameDH4, *frameDH5; frameDH0 = frameCache->spriteFrameByName("DH (0).png"); frameDH1 = frameCache->spriteFrameByName("DH (1).png"); frameDH2 = frameCache->spriteFrameByName("DH (2).png"); frameDH3 = frameCache->spriteFrameByName("DH (3).png"); frameDH4 = frameCache->spriteFrameByName("DH (4).png"); frameDH5 = frameCache->spriteFrameByName("DH (5).png"); frameDH0->autorelease(); frameDH1->autorelease(); frameDH2->autorelease(); frameDH3->autorelease(); frameDH4->autorelease(); frameDH5->autorelease(); CCSprite *sp0 = CCSprite::createWithSpriteFrame(frameDH0); sp0->setPosition(ccp(50, 150)); batchNode->addChild(sp0); CCSprite *sp1 = CCSprite::createWithSpriteFrame(frameDH1); sp1->setPosition(ccp(140, 150)); batchNode->addChild(sp1); CCSprite *sp2 = CCSprite::createWithSpriteFrame(frameDH2); sp2->setPosition(ccp(230, 150)); batchNode->addChild(sp2); CCSprite *sp3 = CCSprite::createWithSpriteFrame(frameDH3); sp3->setPosition(ccp(320, 150)); batchNode->addChild(sp3); CCSprite *sp4 = CCSprite::createWithSpriteFrame(frameDH4); sp4->setPosition(ccp(410, 150)); batchNode->addChild(sp4); CCSprite *sp5 = CCSprite::createWithSpriteFrame(frameDH5); sp5->setPosition(ccp(500, 150)); batchNode->addChild(sp5); return true; }
|
代码中,第33-34行,创建了一个CCSpriteBatchNode
对象,并将其加入场景,在51-68行中,所有的CCSprite
对象全部加入到CCSpriteBatchNode
对象,此时,所有的CCSprite
将不再重新渲染,如下图:
与之前的截图相比,渲染次数减少到2,说明这样的优化方法是可行的。
说明:CCSprite
对象必须加入到CCSpriteBatchNode
中才能起到优化作用,如果直接将CCSprite
加入到场景中,则不会重用CCSpriteBatchNode
的渲染结果。这里可以理解为,CCSpriteBatchNode
是一个虚拟的容器,将此容器加入到场景后,再将CCSprite
加入其中,由CCSpriteBatchNode
对其进行管理,另外,如果CCSprite
使用的资源不包含在CCSpriteBatchNode
初始化的资源中,则程序会出现异常。
结束
程序的优化方法有很多种,但是并不是所有的优化方法都适用于当前的应用场景 ,这也牵涉到优化的性价比的问题,对程序进行适度的优化才是最正确的。
另:由于本人是初学跟前端有关的开发,特别是游戏前端,因此,在我所讨论的内容中难免有所偏颇,希望能够得到大家的指正,谢谢。