自分のアプリは2013年頃から作り始めた関係で、いまやほぼ化石となった Cocos2d-x を使っています。
Cocos2d-x は開発環境が Unity などと比べて貧弱(個人の感想)で、Node のツリー状態を簡単に確認することができず、UI周りで困ることが結構ありました。
Nodeツリーをダンプする処理を自作して使っているので、今回はそれを紹介したいと思います。
Cocos2d-x でのUI周りで困ること
Cocos2d-x は Scene をコード上で生成したりする関係で、Nodeのツリー構造がどうなってるのか把握しづらくなる事が多い気がします。
ダイアログなどの各画面のレイアウトは CocosStudio で作っていますが、結局コード上でレイアウトファイルをロードしてNodeツリーに追加するわけで、意図通りに表示されなくて調査が必要なことが多いです。
もう色々面倒なので、Nodeツリーをダンプするコードを開発初期に用意してそれを使ってデバッグしていました。Unity でいう Hierarchy のツリー相当のものになりますね。
Nodeツリーをダンプするコード
以下がダンプ用のユーティリティ関数になります。dumpNodeTree() が本体ですね。
#include "cocos2d.h"
#include <sstream>
#include <string>
USING_NS_CC;
std::string replaceLineFeedToSpace(const std::string& str)
{
std::string result = str;
size_t pos;
while ((pos = result.find("\r\n")) != std::string::npos) {
result.replace(pos, 1, " ");
}
while ((pos = result.find("\n")) != std::string::npos) {
result.replace(pos, 1, " ");
}
while ((pos = result.find("\r")) != std::string::npos) {
result.replace(pos, 1, " ");
}
return result;
}
void dumpNodeTreeRecursive(const Node* pNode, int& count, const std::string& prevPrefix, bool isLastChild)
{
std::stringstream prefixSs;
std::stringstream nextPrefixSs;
prefixSs << prevPrefix;
nextPrefixSs << prevPrefix;
if (count > 1) {
if (isLastChild) {
prefixSs << " └";
nextPrefixSs << " ";
} else {
prefixSs << " ├";
nextPrefixSs << " │";
}
}
const Vector<Node*>& children = pNode->getChildren();
ssize_t childrenNum = children.size();
const Vec2& pos = pNode->getPosition();
const Size& size = pNode->getContentSize();
std::string prefix = prefixSs.str();
std::string name = replaceLineFeedToSpace(pNode->getName());
std::string description = replaceLineFeedToSpace(pNode->getDescription());
const Vec2& anchor = pNode->getAnchorPoint();
const Vec2& anchorPoint = pNode->getAnchorPointInPoints();
float sx = pNode->getScaleX();
float sy = pNode->getScaleY();
int t = pNode->getTag();
int lz = pNode->getLocalZOrder();
float gz = pNode->getGlobalZOrder();
const char* p = prefix.c_str();
const char* n = name.c_str();
const char* d = description.c_str();
float x = pos.x;
float y = pos.y;
float w = size.width;
float h = size.height;
float ax = anchor.x;
float ay = anchor.y;
float apx = anchorPoint.x;
float apy = anchorPoint.y;
const char* sx1 = ((isLastChild && count > 1) ? "}" : "");
const char* sx2 = (childrenNum > 0 ? "{" : "");
CCLOG("%s #%d <%s> (%.0f, %.0f) %.0fx%.0f, scale:(%.2f, %.2f), anchor:(%.2f, %.2f)<%.0f, %.0f>, tag:%d, LZ:%d, GZ:%.0f, [%s] %s%s", p, count, n, x, y, w, h, sx, sy, ax, ay, apx, apy, t, lz, gz, d, sx1, sx2);
++count;
std::string nextPrefix = nextPrefixSs.str();
for (ssize_t i = 0; i < childrenNum; ++i) {
const Node* pChildNode = children.at(i);
dumpNodeTreeRecursive(pChildNode, count, nextPrefix, (i == childrenNum - 1));
}
}
void dumpNodeTree(const Node* pNode)
{
CCLOG("========");
int count = 1;
std::string prefix("");
dumpNodeTreeRecursive(pNode, count, prefix, true);
CCLOG("========");
}
これを、例えば Scene トップからのNodeツリーをダンプしたい場合は
dumpNodeTree(Director::getInstance()->getRunningScene());
みたいな形で呼び出すと、以下のようにログに出力されます。
========
#1 <> (0, 0) 347x568, scale:(1.00, 1.00), anchor:(0.50, 0.50)<174, 284>, tag:-1, LZ:0, GZ:0, [<Scene | tag = -1>] {
├ #2 <> (0, 0) 0x0, scale:(1.00, 1.00), anchor:(0.00, 0.00)<0, 0>, tag:-1, LZ:0, GZ:0, [<Node | Tag = -1]
└ #3 <TitleScene> (0, 0) 347x568, scale:(1.00, 1.00), anchor:(0.50, 0.50)<174, 284>, tag:999, LZ:0, GZ:0, [<Layer | Tag = 999>] }{
└ #4 <Layer> (174, 284) 640x1136, scale:(0.50, 0.50), anchor:(0.50, 0.50)<320, 568>, tag:1, LZ:1, GZ:0, [<Node | Tag = 1] {
├ #5 <Base> (320, 568) 640x1136, scale:(1.00, 1.00), anchor:(0.50, 0.50)<320, 568>, tag:0, LZ:0, GZ:0, [ImageView] {
│ ├ #6 <Background> (320, 568) 640x1136, scale:(1.08, 1.08), anchor:(0.50, 0.50)<320, 568>, tag:10001, LZ:0, GZ:0, [ImageView]
│ ├ #7 <TitleLogoJp> (320, 693) 1080x344, scale:(0.50, 0.50), anchor:(0.50, 0.50)<540, 172>, tag:20001, LZ:0, GZ:0, [<Sprite | Tag = 20001, TextureID = 6>]
│ ├ #8 <TitleLogEn> (320, 693) 1080x344, scale:(0.50, 0.50), anchor:(0.50, 0.50)<540, 172>, tag:20002, LZ:0, GZ:0, [<Sprite | Tag = 20002, TextureID = 7>]
│ └ #9 <Label> (320, 350) 386x68, scale:(1.00, 1.00), anchor:(0.50, 0.50)<193, 34>, tag:1001, LZ:0, GZ:0, [Label] }
├ #10 <HeaderLayer> (320, 1022) 640x64, scale:(1.00, 1.00), anchor:(0.50, 1.00)<320, 64>, tag:50001, LZ:0, GZ:0, [ImageView] {
│ └ #11 <AdLayer> (320, 78) 640x100, scale:(1.00, 1.00), anchor:(0.50, 0.00)<320, 0>, tag:90001, LZ:0, GZ:0, [ImageView] }
└ #12 <FooterLayer> (320, 0) 640x120, scale:(1.00, 1.00), anchor:(0.50, 0.00)<320, 0>, tag:50002, LZ:0, GZ:0, [ImageView] }{
├ #13 <TimeLabel> (320, 160) 309x36, scale:(1.00, 1.00), anchor:(0.50, 0.50)<155, 18>, tag:10003, LZ:0, GZ:0, [Label]
└ #14 <VersionLabel> (320, 210) 308x36, scale:(1.00, 1.00), anchor:(0.50, 0.50)<154, 18>, tag:10002, LZ:0, GZ:0, [Label] }
========
座標値とか、ツリー構造とかが意図したものになっているかとかを確認できます。
これでもだいぶ見づらいですが、無いよりはマシかと…。
(もっと見やすい形でHTMLで出力するとか考えたこともありましたが、ひとまずそこまでは不要で今までやってこれています)
まとめ
Cocos2d-x のNodeツリーを確認する手段がないので、自作したコードを紹介しました。
エンジン本体側にこういう機能があれば便利なのですが、無いっぽいんですよね。
無いけど必要なモノは自作して使えるようにしておくと、開発しやすくなりますね。
コメント