首页 > 代码库 > Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

1、概述


    在游戏的运行过程中,图形的绘制是非常大的开销。对于良莠不齐的Android手机市场,绘制优化较好的游戏,可以在更多的手机上运行,因此也是优化的重中之重。图形方面的优化主要体现在减少GUP的绘制次数上。这里我们分别从自动优化渲染批次和绘制剔除两个方面来看新版本在绘制上的优化。


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;
}

    这段代码创建了100个图片。我将示例工程中的按钮复制了9个,并将第三个按钮稍作了修改。这样程序会循环创建这10张图片。图片资源如下图所示。



    编译运行程序,我们可以看到下面的运行画面。




    我们关注的是左下角信息的第二行。“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、小结

    总的来说,这两点优化可以说是对程序性能有了极大提升。同时在开发的过程中,也使程序员不必过多的纠结于渲染效率的优化。

相关代码下载:http://download.csdn.net/detail/fansongy/7398941


PS:最近工作比较忙,博客更新的比较少了。忙过了这段,尽量多写些文章补上,以飨读者。

   本篇博客出自阿修罗道,转载请注明出处,禁止用于商业用途:http://blog.csdn.net/fansongy/article/details/26968473