1、概述
在游戏的运行过程中,图形的绘制是非常大的开销。对于良莠不齐的Android手机市场,绘制优化较好的游戏,可以在更多的手机上运行,因此也是优化的重 中之重。图形方面的优化主要体现在减少GPU的绘制次数上。这里我们分别从自动优化渲染批次和绘制剔除两个方面来看新版本在绘制上的优化。
2、自动batch
在Cocos2d-x 3.x中,抛弃了先前手动编写BatchNode,采用自动管理的方式。说起BatchNode,就难免涉及到显卡底层的绘制原理。简单的说,每提交一条 绘制指令到显卡都会产生消耗,因此尽量少的提交指令就可以优化性能。更具体的说,当整个场景绘制都放在同一条指令中时,是最佳的状态。
只介绍理论很难说明问题,我们动手写个Demo做测试。
创建一个新工程。更改init函数如下。- bool HelloWorld::init()
- {
- //
- // 1. super init first
- 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;
- }
编译运行程序,我们可以看到下面的运行画面。
我们关注的是左下角信息的第二行。“GL calls”代表每一帧中OpenGL指令的调用次数。这个数字越小,程序的绘制性能就越好。现在每有101次绘制,其中100个元素每个元素绘制一次,多出来的一次是绘制这个左下角信息自己。
接下来,我们使用合图软件,将这10张图合成一张大图和一个plist文件。在使用CocoStudio导出时,选择“使用大图”即可将小图合成一张大 图。当然我们也可以选择TexturePacker这种专业的合图软件。合成的图片分为“test.png”和“test.plist”两部分,如上面的 资源文件图片所示。
更改init代码如下。
- bool HelloWorld::init()
- {
- //
- // 1. super init first
- 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::create(name);
- 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()
- {
- //
- // 1. super init first
- 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::create(name);
- //auto sprite = Sprite::createWithSpriteFrameName(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、小结
本篇博客出自,转载请注明出处,禁止用于商业用途: