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

フニゲの開発日記

Electronとか...

さらにcc.drawingUtil

cocos2d-html5


今回はRay Wenderlichのこの記事をcocos2d-html5でやりたかったんだけど、
How To Create A Game Like Tiny Wings with Cocos2D 2.X
……OpenGLが難しすぎる。挫折した。
代わりにcc.drawingUtilを使ってポリゴンを描くことにする。

drawSolidPolyで描けるのは、単色のポリゴンだけのようだ。テクスチャを貼ったり、頂点カラーをつけることはできない。
ポリゴンは何角形でも描けるが、TRIANGLE_FANを使っているので、凹形の部分があるとうまく描画できないんじゃないかと思う。もちろんTRIANGLE_STRIPみたいなポリゴンメッシュを一度に描く機能はない。

条件が厳しいときは、制限をゲームのネタとして生かすことを考える。
テクスチャが貼れないなら、真っ白な雪山ということにしてしまえ。

f:id:funige:20131029144305p:plain

まず丘のアウトラインを描いてみよう。

// hill.js
var Hill = cc.Node.extend({
    init:function () {
        this._super();

        this.generateHills();
        return true;
    },

    // ランダムに丘を生成する
    generateHills:function () {
        var points = []
        var x = 0;
        var y = 0;

        for (var i = 0; i < 1000; i++) {
            points[i] = cc.p(x, y);
            x += 720 / 4;
            y = (Math.random() - 0.5) * 360;
        }
        this.hillKeyPoints = points;
    },

    // 丘を描画
    draw:function () {
        cc.drawingUtil.setDrawColor4F(0,0,0,1);
        for (var i = 1; i < this.hillKeyPoints.length; i++) {
            var p0 = this.hillKeyPoints[i - 1];
            var p1 = this.hillKeyPoints[i];
            cc.drawingUtil.drawLine(p0, p1); // とりあえずラインで描く
        }
    },
});
...

// game.js
this.hill = Hill.create();
this.addChild(this.hill);
...

f:id:funige:20131029180928p:plain

プレイヤーと地面の当たりはどうすればいいのかな。
元ネタではBox2Dを使っている。これで正解なのだが、ここではもっと泥臭い方法を考える。

プレイヤーのいる場所の地面の高さを返す関数を作ればいいのだ。

// hill.js
    ...
    prevIndex:1, // 高速化のため前回の場所から探す

    heightAt:function (x) {
        var i = this.prevIndex;
        while (true) {
            if (this.hillKeyPoints[i].x < x) {
                if (i < this.hillKeyPoints.length) {
                    i++;
                    continue;
                }
            } 
            if (this.hillKeyPoints[i - 1].x > x) {
                if (i > 1) {
                    i--;
                    continue;
                }
            }
            break;
        }
        this.prevIndex = i; // プレイヤーは今i-1とiの間にいる
        
        var p0 = this.hillKeyPoints[i - 1];
        var p1 = this.hillKeyPoints[i];

        // これが足下の地面の高さ
        return p0.y + (p1.y - p0.y) * (x - p0.x) / (p1.x - p0.x);
    },
    ...

こんな感じで。

それにしても重い。毎秒13.6フレームしか描けていない。
これは画面の外を描かないようにすれば改善できるだろう。
hill.jsのdraw関数を書き直す。
あとラインの代わりにポリゴンを使ってみた。

// hill.js
    ...
    draw:function () {
        var segments = 50; // 細長いポリゴンを50枚並べて丘を描画する

        var offset = this.getPosition().x;
        var min = -offset - g.size.width / 2; // 画面左端のx
        var max = -offset + g.size.width / 2; // 画面右端のx
        var step = (max - min) / this.segments;

        for (var i = 0; i < this.segments; i++) {
            var x0 = min + step * i;
            var y0 = this.heightAt(x0);
            var x1 = x0 + step;
            var y1 = this.heightAt(x1);

            var verts = [ 
                cc.p(x0, y0), 
                cc.p(x1, y1), 
                cc.p(x1, -1000), 
                cc.p(x0, -1000),
            ];
            cc.drawingUtil.drawSolidPoly(verts, verts.length, cc.c4f(0,0,0,1));
        }
    },

f:id:funige:20131029192014p:plain

60フレームになった。
横に50分割するのは過剰だが、これは丘を丸くするためだ。
heightAt()の最後の行を書き換える。

// hill.js
    heightAt:function () {
        ....
        //return p0.y + (p1.y - p0.y) * (x - p0.x) / (p1.x - p0.x);

        // cos関数を使って斜面を丸くする
        var r = Math.PI * (x - p0.x) / (p1.x - p0.x);
        return p0.y + (p1.y - p0.y) * (1 - Math.cos(r)) / 2;
    },

動くサンプルはこちら。

広告を非表示にする