読者です 読者をやめる 読者になる 読者になる

筋肉とエンジニアリングで すべてを解決するブログ

筋トレ、JavaScript、Ruby で世界を変えてやります。

C++ と Cocos2d-x でよく使うメソッド等

f:id:ma3tk:20140412223423j:plain

C++ 関係でよく使うもの

詳しくは wiki とか見ると勉強になります

http://ja.wikipedia.org/wiki/C%2B%2B11

PHPer にとっては注意すべき if 文 (主に C とか C++ 忘れてたり、書いたこと無い僕用の注釈w)

if 文って 100% 使いますよね。

ただし PHPer にとって癖があるなって思ったのが、 if の { } 内で auto var1 = 100; とかってやっても、 if の外に出るとこの var1 は使えなかったりします。

つまり、

int var1;
if (hoge) { 
    var1 = 100; 
}

ってやらなきゃいけない所があります。

PHP しかやってない人だとそう感じるけど、C言語は当たり前のことですのでそこをお忘れなく。

面倒な分、スコープを小さくすることができるというメリットがあります!

C++11 から便利になった for 文

int my_array[5] = {1, 2, 3, 4, 5};
for (int& x : my_array) {
    x *= 2;
}

いちいちあの煩わしい文を書かなくていい!

理解するまでに時間がかかるけど即時関数として便利なラムダ式

// C++ での標準的な例
[](int x, int y) -> int { int z = x + y; return z + x; }

上記のように書けます。

で、例えば Cocos2d-x でのコールバックを使った書き方は

auto bulletEffect = CallFunc::create(CC_CALLBACK_0(Base::playBulletEffect, this));

ですが、上記の式の代わりにラムダ式で書くと…

auto bulletEffect = CallFunc::create([=](){
    // do something
});

のように書けます。

応用編

ラムダ式は 内部にある関数みたいなものなので、実行順序等に注意!

auto childSprite = Sprite::create();
int number = 0;
auto hoge = CallFunc::create([=, &number](){
    CCLOG("test2, %d", number);
    number++;
    auto fuga = CallFunc::create([=, &number](){
        CCLOG("test3, %d", number);
        number++;
    });
    childSprite->runAction(fuga);
});
CCLOG("test1, %d", number);
number++;
runAction(hoge);
CCLOG("test4, %d", number);
number++;

もちろん上記の hoge や fuga は宣言されているだけで実際に callback として呼ばれた時にそのコードが動きます。

上記のような場合、式の実行順序、variable{} の variable がきちんと意図したタイミングで渡せるかどうか注意すべきポイントです。

普通の変数で渡さずに pointer で渡す事ももちろんできるので把握しておいたほうが良いですね。それと、[=] や [&] や[val1, &val2] 等どう違うのか知っておくといいと思います。 基本 [=] で事足りますが。

実行時に高速化するための const

書き換えが必要ない場合は const 使いましょう!他の言語でも同様だと思いますが。

dynamic_cast<型>(変数); による型変換

ある型の変数を その子クラスの型にキャストする時に使います。

getChildrenByName("child1"); で addChild() 済みの Node型の子が取れるが、Layer に変換したい時とかも使いますね! (getChildrenByName<Layer*>("child1"); でいけます)

また、自分で定義したクラスとそれを継承したクラスでの変換に便利ですね。

ただし、型変換できなかった場合は、NULL になるはずなので注意して使ってください。

また、 static_cast もきちんと見ておいて、違いは何なのか知っておいてください。

C++ の逆に使わないやつ

this の省略

.h 内で変数を定義しておけば グローバルに変数名が使えるので this 地獄にならなくて済みます。

逆に変数定義しすぎてグローバル汚染にならないように気をつけてください。

僕はあるアプリを作ってる時に途中まで知らなくて this 地獄になったのでリファクタリングしたいです。

Cocos2d-x でよく使う奴ら

CC_CALLBACK_0(Func::function, this) とかのマクロ

コールバック関数。

// new callbacks based on C++11
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)

コールバックについてそもそも知ってる?ってところな方もいなくはないかと思いますが。

コールバック関数は便利なんですが、詰まりポイント、やらかしポイントとして、時間に差異があることで object が破棄されていて 中身が NULL だったりすることも大いに有るので通常のデバッグ以上に注意しましょう。

CCLOG("%d %f", int_value, float_value, ........);

update() とかの常時呼ばれるものだったり、内容が多いループとかでいちいちデバッガ使いたくない時にはこれを使ってコンソールに出してみるとGood。

一回くらいのプリントしたい場合は デバッガで breakpoint 張ってチェックした方がいいですね。

もちろん #include して cout << var1 してもいいけど、CCLOG も cout もファイルの入出力なので必要時以外は消しておくといいと思います。

Release build の時には削られるので心配ないですが、デバッグ時にすげー落ちるけどリリースビルドを触ると全然動く、みたいなこともあったりします。

ClassName::functionName() と functionName() の違い

インスタンス化されたそれかそうじゃないかかな?

const の場合は きちんと明示して .cpp に書いておきたいですね。

localZOrder と globalZOrder について

基本 local を使って使って管理すると良いです。

local は同じレベル間での重なりの順序。

global はそれに関係なく全てでグローバルな重なりの順序。

描画に関するあれこれあるので、なるべく local でやると多数の要素を描画しなくてはならない時にミスが少なくなるかと思います。

Z-Order の設計は気をつけてください。

setVisible(bool)

ただ単に setVisible して見えなくしたりするだけだと動作が重くなる原因になるので注意した方がいいです。

removeFromParent() を自身で呼ぶか、 removeChildByName() で親クラスから メモリから解放したりしてあげる必要がありますね。

たぶんそうしないと 描画のために走査はされるが 表示されないだけなので、毎フレームごとに 中身は呼ばれてるんちゃうかと思いますが、未検証です。

setPosition(Vec2)

表示する位置を決めます。

親のノード からどれくらいの位置に動かすのかという話であって、画面全体の Vec2(0, 0) からのどの位置なのかという話ではないです。

もちろん、親のノードの Position が setPosition(Vec2(0, 0)) の場合であればそういう話になりますが。

親ノードに影響されて位置が決まるので、Scene, Layer, Node の設計はある程度早い段階から考えて影響が内容にすることが重要です。

ついでに anchorPoint についても理解しておくといいと思います。

真ん中揃えが基本で、どの位置を基準にするかって話です。

Position については cocos studio を使って Node の位置と Position あたりに触れておくと理解がすごい早いです。

上位レイヤーに Node 変更すると位置もぜんぜん変わるのがわかるかと思います。

なんでセットされたポジションに表示されないのかとかはそもそも

  • 自身を addChild() した上の親クラスが addChild() されていななかった
  • 画面外にセットされてしまっている
  • setVisible(false) になってしまっている
  • setOpacity(0) にしてしまっている
  • setPosition(Vec2) の Vec2 が 思っていた値じゃなくて Vec2(0, 0) になっていて左下に描画されている

など。が考えられますのでバグったら確認してみてください。

アニメーション周りのネタ

Spawn, FadeIn, FadeOut, FadeTo, Sequence, RepeatForever, MoveTo(By), ScaleTo(By), DelayTime, CallFunc あたりはよく使いますね。

runAction() 時に、 Vector<FiniteTimeAction*> を作って array で渡しても OK なように作られています。ループで同じようなアニメーションを作りたい時に便利そうです。

runAction() の Sequence とか Spawn の罠

上述しましたが、

runAction(Sequence::create(Spawn::create(hoge, 
                                         fuga,
                                         NULL), 
                           Spawn::create(aaa, 
                                         bbb, 
                                         DelayTime::create(0.5f), 
                                         CallFunc::create([=](){ 
                                             hogehoge(); 
                                         }), 
                                         NULL), 
                           CallFunc::create([=](){
                               fugafuga(); 
                           }), 
                           NULL)
         );

こういった複雑な挙動の場合どう動くかちゃんと理解しておくといいと思います。