Lento con forza

大学生気分のIT系エンジニアが色々書いてく何か。ブログ名決めました。

cocos2d-xで尋常じゃない数の線を引きたい時

最近cocos2d-x v3.3でゲーム作ってます。
ゲームの実装で、タッチの動きに合わせて線を引きたかったのですが、素直に実装したらとんでもないことになりました。

DrawNodeで実装

まず最初は、タッチ位置が移動する度にDrawNodeのdrawSegumentを呼び出す方法で実装しました。コードにするとこんな感じです。

    auto drawNode = DrawNode::create();
    this->addChild(drawNode);
    
    auto listener = EventListenerTouchOneByOne::create();
    listener->onTouchBegan = [this](Touch* touch, Event* event) {
        auto pos = touch->getLocation();
        this->prevPos = pos;
    
        return true;
    };
    listener->onTouchMoved = [drawNode, this](Touch* touch, Event* event) {
        auto pos = touch->getLocation();
        drawNode->drawSegment(prevPos, pos, 10, Color4F(1,1,1,1));
        
        this->prevPos = pos;
    };
    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);

これで一応要件は満たせます。後はcocos2d-xが内側でいい感じに処理をしてくれればいいなーって考えてたんですけど、実行するとこんな感じに。

f:id:kouki_dan:20150211184054p:plain

注目すべきは左下です。

左下のフレームレートとGL Vertsの数がヤバイことに。
元は60フレームだったのに、線の描画が重くて10フレームまで落ちてます。
これじゃあ使えないなぁと思い別の方法を探索。

RenderTextureで実装

DrawNodeは毎回線全部を描画しているのが問題なので(多分)、別の方法で描画してあげれば処理が早くなりそうです。
調べた感じ、RenderTextureを使えばこの問題を解決できそうだったのでこれを採用することに。
グラフィックの世界の専門用語はよくわからないですが、オフスクリーンレンダリングと呼ばれる手法らしいです(間違ってたらごめんなさい)
DrawNodeで線を描画して、RenderTextureにレンダリング といった処理を繰り返す実装にしてみました。

以下コードはこんな感じ。

    Size size = Director::getInstance()->getVisibleSize();
    auto renderTexture = RenderTexture::create(size.width, size.height);
    renderTexture->setPosition(Vec2(size.width/2, size.height/2));
    this->addChild(renderTexture);
    
    auto listener = EventListenerTouchOneByOne::create();
    listener->onTouchBegan = [this](Touch* touch, Event* event) {
        auto pos = touch->getLocation();
        this->prevPos = pos;
        
        return true;
    };
    listener->onTouchMoved = [renderTexture, this](Touch* touch, Event* event) {
        auto pos = touch->getLocation();
        
        renderTexture->begin();
        auto drawNode = DrawNode::create();
        drawNode->drawSegment(prevPos, pos, 10, Color4F(1,1,1,1));
        drawNode->visit(); // RenderTextureにレンダリング
        renderTexture->end();
        
        this->prevPos = pos;
    };
    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);

こちらの実行結果はこんな感じです

f:id:kouki_dan:20150211185230p:plain

見た目は一緒ですが中身と速度は全く違います。

フレームレートが30になってしまったのですが、最初から30だったので問題なし(?)です。
RenderTextureを使うと30よりあがらない制限があるのかな・・?詳しくは調べてないからわからないです。

線を描画することによって処理が重くなっている様子もなく、ゲームにも使えそうです!
DrawNodeが重いわ!!って方はRenderTextureを使って描画する方法もお試しください!