CocosStudioで作ったLayerの子要素を自作のLayerに載せ替える

CocosStudioで作成で作成したLayerの子要素を自作したLayerに乗せ換える話です。

CocosStudioで「Layer」を作成し、csb形式でpublish後にCocos2d-x側から、そのレイヤーを呼び出します。そうすると、取得できる型がcocos2d::Node*型で取得できます。CocosStudioでこんな画面から作るやつです。

f:id:Takachan:20180425224230p:plain

#include "cocostudio/CocoStudio.h"
// ...中略...
Node* layer = CSLoader::getInstance()->createNode("layer.csb");

このNodeから子要素をgetChildByTag()や、getChildByName()で取得し、それらにアニメーションやイベントを設定しても良いのですが、このコードがあるメソッドのコード量が大変多くなってしまいます。というか、LayerやNodeを継承した自作のクラスに子要素が乗っていた方が何かと便利です。(データ管理的な意味で

また、戻り値のNodeですが、Layerにアップキャストできない単なるNode型となっています。

というわけで、自作のクラスにNodeに乗っている子要素を移し替えたいと思います。

先ずは自作のLayerクラスを用意します。

// MyLayer.hpp
#pragma once

#include "cocs2d.h"
#include "CreateFunc.hpp"
class MyLayer : public cocos2d::Layer, CreateFunc<MenuLayer>
{
public:
    init();
    using CreateFunc::create;
}

【補足】CreateFuncについてはこちらを参照ください。

次に実装側です。

bool MyLayer::init()
{
    if (!Layer::init())
    {
        return false;
    }

    // CocosStudioで作成したLayerの呼び出し
    Node* node = CSLoader::getInstance()->createNode("Layter.csb");

    // nodeの子要素をそのまま全て自分に移し替える
    std::vector<cocos2d::Node*> tempvec;
    for (auto n : node->getChildren())
    {
        tempvec.push_back(n);
    }

    source->removeAllChildren();

    for (cocos2d::Node* n : tempvec)
    {
        dest->addChild(n);
    }

    return true;
}

これで、後はSceneを作成するところで普通にMyLayer::Create()してやれば自作のクラスに要素が移っています。

定型的な処理なので以下のようにUtilityにしておいてもいいと思います。

// base → targetに全てのノードを移動します。(Globalなstatic関数として定義)
static void moveChildNodes(cocos2d::Node* source, cocos2d::Node* dest)
{
    std::vector<cocos2d::Node*> tempvec;
    for (cocos2d::Node* n : source->getChildren())
    {
        tempvec.push_back(n); // 子要素を列挙して記録しておく
    }

    source->removeAllChildren(); // 元の親要素から削除

    for (cocos2d::Node* n : tempvec)
    {
        dest->addChild(n); // 新しい要素に付け加える
    }
}

また、CocosStudioで作成したLayerの大きさと、Cocos2d-x側の画面の大きさが違うと位置がずれるため、上記に各々の比率を見て表示位置を変更する処理を追加すると柔軟性が増すと思います。ちゃんと確認していないですが、以下のようにすれば位置も縮尺によって追従すると思います。

static void moveChildNodes(cocos2d::Node* source, cocos2d::Node* dest, bool followingSize = false/*←New*/)
{
    std::vector<cocos2d::Node*> tempvec;
    for (cocos2d::Node* n : source->getChildren())
    {
        tempvec.push_back(n);
    }

    source->removeAllChildren();

    for (cocos2d::Node* n : tempvec)
    {
        dest->addChild(n);

        // ★サイズ追従をするときだけ位置を変更
        if (followingSize)
        {
            Size sourceSize = source->getContentSize();
            Size destSize = dest->getContentSize();

            float newx = n->getPosition().x * destSize.width / sourceSize.width;
            float newy = n->getPosition().y * destSize.height / sourceSize.height;

            n->setPosition(Vec2(newx, newy));
        }
    }
}

最後に、この話、何もCocosStudioで作ったLayerでなくても任意のNode間で実行できるので、まぁ、、、他でもそういった用途に使用できます。