在Cocos2d-x 3.x中,抛弃了先前手动编写BatchNode,采用自动管理的方式。说起BatchNode,就难免涉及到显卡底层的绘制原理。简单的说,每提交一条 绘制指令到显卡都会产生消耗,因此尽量少的提交指令就可以优化性能。更具体的说,当整个场景绘制都放在同一条指令中时,是最佳的状态。
只介绍理论很难说明问题,我们动手写个Demo做测试。
创建一个新工程。更改init函数如下。
- bool HelloWorld::init()
- {
-
-
- if ( !Layer::init() )
- {
- return false;
- }
- Node* node = Node::create();
- char name[32];
- for(int i = 0;i<100;++i)
- {
- memset(name, 0, sizeof(name));
- sprintf(name, "%d.png",i%10);
- auto sprite = Sprite::create(name);
- sprite->setPosition(Point(i*5,i*5));
- node->addChild(sprite, 0);
- }
- this->addChild(node);
- return true;
- }
这段代码创建了100个图片。我将示例工程中的按钮复制了9个,并将第三个按钮稍作了修改。这样程序会循环创建这10张图片。图片资源如下图所示。
编译运行程序,我们可以看到下面的运行画面。
我们关注的是左下角信息的第二行。“GL calls”代表每一帧中OpenGL指令的调用次数。这个数字越小,程序的绘制性能就越好。现在每有101次绘制,其中100个元素每个元素绘制一次,多出来的一次是绘制这个左下角信息自己。
接下来,我们使用合图软件,将这10张图合成一张大图和一个plist文件。在使用CocoStudio导出时,选择“使用大图”即可将小图合成一张大
图。当然我们也可以选择TexturePacker这种专业的合图软件。合成的图片分为“test.png”和“test.plist”两部分,如上面的
资源文件图片所示。
更改init代码如下。
- bool HelloWorld::init()
- {
-
-
- if ( !Layer::init() )
- {
- return false;
- }
- CCSpriteFrameCache::getInstance()->addSpriteFramesWithFile("test.plist","test.png");
- Node* node = Node::create();
- char name[32];
- for(int i = 0;i<100;++i)
- {
- memset(name, 0, sizeof(name));
- sprintf(name, "%d.png",i%10);
-
- auto sprite = Sprite::createWithSpriteFrameName(name);
- sprite->setPosition(Point(i*5,i*5));
- node->addChild(sprite, 0);
- }
- this->addChild(node);
- return true;
- }
这
段代码中,我们调用addSpriteFramesWithFile函数,将大图载入到内存中,创建对象时,调用
createWithSpriteFrameName从缓存纹理中载入图片。如此做我们所有的绘制调用都可以合并到一次OpenGL指令中,这些绘制指令
的计算与合并都由Cocos2d-x引擎完成。编译运行如下图所示。
我们可以非常明显的看到,优化后的程序“GL calls”变成了2次。
3、绘制剔除
另一方面优化是绘制剔除。相对于上一种优化,这个要更容易理解。它是指当一个元素移动到屏幕之外,就不进行绘制。
接着刚才的例子,我们测试一下这个特性。更改init函数如下。
- bool HelloWorld::init()
- {
-
-
- if ( !Layer::init() )
- {
- return false;
- }
-
- Node* node = Node::create();
- char name[32];
- for(int i = 0;i<100;++i)
- {
- memset(name, 0, sizeof(name));
- sprintf(name, "%d.png",i%10);
- auto sprite = Sprite::create(name);
-
- sprite->setPosition(Point(i*5,i*5));
- node->addChild(sprite, 0);
- }
- this->addChild(node);
-
- auto listener = EventListenerTouchOneByOne::create();
- listener->onTouchBegan = [=](Touch *pTouch, Event *pEvent)
- {
- return true;
- };
- listener->onTouchMoved = [=](Touch *pTouch, Event *pEvent)
- {
- node->setPosition(node->getPosition()+pTouch->getDelta());
- };
- Director::getInstance()->getEventDispatcher()->
- addEventListenerWithSceneGraphPriority(listener, this);
-
- return true;
- }
首先,我们将自动Batch的优化更改回来,否则无法进行测试。接下来,我们在场景中加入一个点击事件,点击拖动屏幕时,移动这100个元素。编译运行,运行效果如下图。
可以看到,当部分图片被移出屏幕时,“GL calls”的数量会下降。
4、小结
总的来说,这两点优化可以说是对程序性能有了极大提升。同时在开发的过程中,也使程序员不必过多的纠结于渲染效率的优化。
相关代码下载:http://download.csdn.net/detail/fansongy/7398941
PS:最近工作比较忙,博客更新的比较少了。忙过了这段,尽量多写些文章补上,以飨读者。