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

フニゲの開発日記

Electronとか...

ctorとかinitとかonEnterとか

cocos2d-html5


cc.Layerを継承するとき、初期化のコードはどこに書いたらいいのか。
ここで初期化というのは、例えばスコアを0に初期化したり、レイヤーにスコアを表示するラベルを貼り付けることなんだけど、どこに書くのが正しいのか。

cc.Layerを継承したクラスでは、インスタンスの作り方はこんな感じ。

Game.create = function () {
    var layer = new Game();
    if (layer && layer.init()) {
        // ここで初期化しても動くことは動くんだけど
        // クラスの機能ならクラスの中に書くべきだと思う
        ...
        return layer;
    }
    return null;
};

createの冒頭で、ctorとinitが(この順番で)呼び出されることがわかる。

・ctorはnew Game() の中で暗黙的に呼ばれるのに対し、
 initは自分で明示的に呼ばないといけない。
・ctorの返り値は無いが、initはtrueまたはfalseを返す。

だがそもそも、なぜctorとinitの2つがあるのだろう。

cocos2dをjavaScriptに移植したとき、開発者達はクラスの継承を実現するためにJohn ResigのSimple Inheritanceを使うことに決めたわけだが、Simple Inheritanceではコンストラクタの名前がたまたまinitだったので、衝突を避けるためにctorという名前に変えたのだと思う。
推測なんだけどね。
ctorの方は「まあ使わなくてもいいけど、あると便利だから一応名前を変えて残しておこう」みたいな感じだったかも知れない。

そういうわけで、ctorはオーバーライドするべきじゃない。
昔はcc.associateWithNativeをctorで呼ぶ必要があったみたいだけど、今はその必要もない。ctorが呼ばれるタイミングでは、親クラスの初期化がまだ終わっていないのだから、ユーザーはinitを使うべきだと考える。

var Game = cc.Layer.extend({
// ctorは使わない
//  ctor:function () {
//      this._super();
//  },

    init:function () {
        this._super();

        // ここで初期化しよう
        ...
        return true;
    },
    ...
});

cc.Nodeやcc.Spriteの継承も同様。

cc.Classから新しいクラスを作る場合には、親クラスが無いのでctorを使っても問題無いだろう。ctorで初期化すれば、initを明示的に呼ぶ必要がないのでコードが簡潔になる。でもこれは混乱の元かなあ。

最後にonEnter()だが、これはiOSのviewWillAppearに似ている。
ctorとinitはインスタンスが作られるときに一回だけ呼ばれるが、onEnterは何度も呼ばれる可能性がある。

    onMouseDown:function (event) {
        if (this.test == undefined) {
            this.test = cc.Sprite.create("res/test.png");
            this.test.onEnter = function () { cc.log("on enter"); };
            this.test.onExit = function () { cc.log("on exit"); };
        }
        this.addChild(this.test);
    },

    onMouseUp:function (event) {
        this.removeChild(this.test);
    },

この例では、addChildされるたびにthis.testのonEnterが呼ばれ、removeChildされるたびにonExitが呼ばれることが分かる。


このへんあいまいだったので、過去の記事では変なコードをたくさん書いてしまった。全部訂正するのは大変だな。どうしようかな。

広告を非表示にする