2016年2月12日金曜日

Cocos2d-xのサンプルコードLens3DのJumpByが止まってしまう件

Cocos2d-xの勉強のためサンプルコードをいくつか動かしてみた。
※サンプルコードを動かす方法はこちら

その中でも興味を引いたのは19. Effects - Advanced にあったJumpy Lens3D


これを使えば面白いものが作れそう!

実際にどのような処理を行っているのかコードを読んだところ

 void Effect4::onEnter()
{
    EffectAdvanceBaseTest::onEnter();
    //Node* gridNode = NodeGrid::create();
    
    auto lens = Lens3D::create(10, Size(32,24), Vec2(100,180), 150);
    auto move = JumpBy::create(5, Vec2(380,0), 100, 4);
    auto move_back = move->reverse();
    auto seq = Sequence::create( move, move_back, nullptr);

    /* In cocos2d-iphone, the type of action's target is 'id', so it supports using the instance of 'Lens3D' as its target.
        While in cocos2d-x, the target of action only supports Node or its subclass,
        so we make an encapsulation for Lens3D to achieve that.
    */

    auto director = Director::getInstance();
    auto pTarget = Lens3DTarget::create(lens);
    // Please make sure the target been added to its parent.
    this->addChild(pTarget);
    //gridNode->addChild(pTarget);

    director->getActionManager()->addAction(seq, pTarget, false);
    
    _bgNode->runAction( lens );
}

Lens3Dのインスタンスを作成してそれをLens3DTargetというNodeを継承したクラスでラップしてActionManagerへ渡している。
このLens3DTargetは本当にただラップしているだけ、ソース上の説明にある通りActionの操作対象はNodeかそのサブクラスである必要があるためらしい。JumpByからsetPosition()とgetPosition()が呼ばれたらメンバであるLens3Dの同メソッドを呼んでいる。

class Lens3DTarget : public Node
{
public:
    virtual void setPosition(const Vec2& var)
    {
        _lens3D->setPosition(var);
    }
    
    virtual const Vec2& getPosition() const
    {
        return _lens3D->getPosition();
    }
    
    static Lens3DTarget* create(Lens3D* pAction)
    {
        Lens3DTarget* pRet = new (std::nothrow) Lens3DTarget();
        pRet->_lens3D = pAction;
        pRet->autorelease();
        return pRet;
    }
private:

    Lens3DTarget()
        : _lens3D(nullptr)
    {}

    Lens3D* _lens3D;
};

基本は分かったので試しにSeaquenceに渡すmoveを一つ増やしてみたところ、
追加分のmoveが動かない

理由も全く分からない。
そして、デバックを繰り返したところやっと理由が判明!

Lens3D::create(10, Size(32,24), Vec2(100,180), 150)

durationとして10を渡していたのが原因だった。

static Lens3D* create (float duration, 
const Size & gridSize, 
const Vec2 & position, 
float radius 
)
static
Create the action with center position, radius, a grid size and duration. 
Parameters
durationSpecify the duration of the Lens3D action. It's a value in seconds. 
gridSizeSpecify the size of the grid. 
positionSpecify the center position of the lens. 
radiusSpecify the radius of the lens. 


このdurationはこのLens3Dの存在時間を指定している。
この値はActionManagerはupdate()の中で操作しているactionが終わったかどうかをisDone()問いうメソッドを通して確認をしている。

このisDoneでは実際に経過した時間と指定したdurationを比べてdurationが経過した場合このActionは終わったとしてtrueを返すため、ActionManagerよりremoveAction(Lens3Dのインスタンス)でLens3Dが管理対象から外されてしまう。

そのため、一回のJumpByが5秒であるmoveの3回目は丁度タイミングよく実行されなくなる。


あー、ここまで調べるのに3日以上かかってしまった。。。