そして cocos2d-x に帰ってくる

あれから結局 CocosSharp はまだ早いと断念し、本家 cocos2d-x に乗り換えました。
さすがに本家では基本的な機能で問題が出ることはなかったのですが、
いくつかの問題があったのでまとめて起きます。

ClippingNode::setStencil で SIGABRT になる

また ClippingNode かい、という感じです。
ちゃんと原因がわかると納得なんですが、同様のタイミングでエラーになった方を
検索してもあまり見かけなかったので、参考までに。

再現させるコードを簡潔に書くと、こんな感じ。

auto clip = ClippingNode::create();
addChild(clip);

auto mask = DrawNode::create();
clip->setStencil(mask);

auto move = MoveBy::create(...);
auto callfunc = CallFunc::create([clip]{
	auto newMask = DrawNode::create();
	clip->setStencil(newMask);
});
clip->runAction(Sequence::create(move, callfunc, NULL));

まず、マスク用のイメージは DrawNode を使い、動的に生成します。
その後、stencil を書き換える CallFunc を実行します。
すると以下のエラーメッセージを出力する CCASSERT に引っかかります。

Node still marked as running on node destruction! Was base class onExit() called in derived class onExit() implementations?

setStencil の中で、古い stencil に対して CC_SAFE_RELEASE が実行され、
参照カウンタが 0 になった場合はそのままデストラクタが呼ばれるという流れになるのですが、
stencil ノードが表示中の為、ASSERT に引っかかってしまうという事の用です。

最終的に DrawNode のインスタンスを保持しておいて、マスク用のイメージを書き換える事で回避しました。

auto clip = ClippingNode::create();
addChild(clip);

auto mask = DrawNode::create();
clip->setStencil(mask);

auto move = MoveBy::create(...);
auto callfunc = CallFunc::create([clip, mask]{
	// 既存のDrawNodeを書き換える
	mask->clear();
	mask->drawPolygon(...)
});
clip->runAction(Sequence::create(move, callfunc, NULL));

基本的な事なのかもしれませんが、
addChildやremoveChildでは SIGABRT になる事はなかったので少し面食らいました。
ちなみにアクション中に限らず、onTouchBegan 中に同様に setStencil すると SIGABRT が起きます。
ClippingNode のステンシルノードは入れ替えたりしない想定で使いましょう、という事で。

RemoveSelfの後のアクションが実行されない事がある

これも基本的な事っぽいのですが、
以下のコードだと “action completed.” はログ出力されません。

auto move = MoveBy::create(...);
auto remove = RemoveSelf::create();
auto delay = DelayTime::create(1);
auto cb = CallFunc::create([=]{
	log("action completed.");
});
node->runAction(Sequence::create(move, remove, delay, cb, NULL));

これは、RemoveSelf の実行後、nodeがどこからも参照されていなければ一定のタイミングで
メモリ上から解放され、その後のアクションは実行されないからです。
間に DelayTime を挟んでいなければ実行されるとは思いますが、あまり信用しない方がいい気がします。

画面上から Node を削除した後 CallFunc を実行した場合は、下記の様に記述するのが正解のようです。

auto move = MoveBy::create(...);
auto hide = Hide::create();
auto delay = DelayTime::create(1);
auto cb = CallFunc::create([=]{
	log("action completed.");
});
auto remove = RemoveSelf::create();
node->runAction(Sequence::create(move, hide, delay, cb, remove, NULL));

cocos2d-xでの本格的な開発は経験がないので、
オープンソースのコードを参考にさせてもらいたいのですが、
ゲームはあまりオープンソースでコードを公開されるケースが少ないですね。
特許だったりチートされる可能性だったり色々あるので仕方ないのですが・・・

またネタが溜まったら書きます。