2015年10月30日

Cocos2d-JS > ADD COIN AND OBSTACLES INTO OUR GAME

原文はこちらです。

イントロダクション

このチュートリアルでは、我々はコインと障害物を我々のパルクールゲームに追加しようと思います。

このチュートリアルの後で、我々のプレイヤーは、走るとコインを集めることができ、障害物に衝突すると死ぬようになります。

我々はまた、タイルドマップエディタでゲームレベルをデザインする方法もカバーします。ゲームのロジックが以前より少し複雑になるので、新しいゲームコンポーネントを追加する前に、コードをリファクタリングします。

準備

始める前に、準備する物事を終えてしまいましょう。

リソースとグローバルのセットアップ

我々は2つの更なるゲーム要素を我々のパルクールゲームに追加します。そのため、幾つかの更なるグローバルな整数タグを、それぞれのゲームアイテムを識別するために追加する必要があります。

以下のコード片を globals.js の最後に追加しましょう:

// chipmunk の衝突タイプ
if(typeof SpriteTag == "undefined") {
    var SpriteTag = {};
    SpriteTag.runner = 0;
    SpriteTag.coin = 1;
    SpriteTag.rock = 2;
};

ここで我々は 0,1,2 をランナー、コインそして岩を表現するために使います。

我々はまた、background.png 及び background.plist と名付けられたもう1つのスプライトシートも導入します。我々はコインと岩のスプライトを background.png と名付けられたスプライトシートにまとめました。

これらのスプライトのまとめ方の詳細は、次のサブセクションからは除外されています。

次に、リソースファイルを我々の res ディレクトリにコピーし、更に2つの更なる変数を更なる参照のために追加します。

var res = {
    helloBG_png : "res/helloBG.png",
    start_n_png : "res/start_n.png",
    start_s_png : "res/start_s.png",
    PlayBG_png  : "res/PlayBG.png",
    runner_png  : "res/running.png",
    runner_plist : "res/running.plist",
    map_png : "res/map.png",
    map00_tmx : "res/map00.tmx",
    map01_tmx : "res/map01.tmx",
    background_png :"res/background.png",
    background_plist : "res/background.plist"
};

var g_resources = [
    //画像
    res.helloBG_png,
    res.start_n_png,
    res.start_s_png,
    res.PlayBG_png,
    res.runner_png,
    res.runner_plist,
    res.map_png,
    res.map00_tmx,
    res.map01_tmx,
    res.background_png,
    res.background_plist
];

コインと岩をスプライトシートに TexturePacker でパック

以前の章にて、我々は小さなスプライトの束を大きくて広くてコンパクトなスプライトシートへのまとめ方を学びました。もう1つのスプライトシートもまとめましょう。

まず最初に、あなたは TexturePacker を起動し、res/TexturePacker/coins 及び rocks ディレクトリ下の全てのアセットをドラッグすべきです。 (注記:あなたは以前のように download からゲームリソース全体を入手できます。)

リソースをドラッグ後、あなたは Data fileTexture formatxxx/chapter8/res/background.png あるいは xxx/chapter8/res/background.plist のようなパスで指定すべきです。

もしあなたがスプライトシートの最適化をしたくないなら、それらをとばして、最終的なスプライトシートを生成するために Publish を押下して下さい。

TiledMap オブジェクトレイヤの導入

我々はレベルマップのために TiledMap を使ってきましたが、ゲームアイテムが欠けています。そのためこのセクションでは、我々はレベルアイテムを TiledMap オブジェクトレイヤにデザインする方法をカバーします。

コインオブジェクトレイヤの追加

まず最初に、コインオブジェクトレイヤを追加します。

  1. Tiled を起動し、map00.tmx 及び map01.tmx を開いて下さい。
  2. map00.tmx 及び map01.tmx にて coin と名付けられたオブジェクトレイヤを作成して下さい。
  3. オブジェクトレイヤを、矩形オブジェクトをマップにドラッグ&ドロップすることでデザインして下さい。
    あなたは矩形のサイズとその位置を変更できます。あなたはまた、オブジェクトを複製したり削除できます。
  1. オブジェクトレイヤをデザインする上での tips:あなたはタイルドマップにてレイヤの透明度を変更することができ、あなたは容易にオブジェクトを配置することができます。

岩オブジェクトレイヤの追加

岩オブジェクトレイヤを作成するためのプロセスは、コインオブジェクトレイヤを作成するのと多くもなく少なくもなく同じくらいです。

そのため我々はそれをあなた自身の実装のままとします。

BackgroundLayer クラスのリファクタリング及び幾つかのヘルパーメソッドの追加

ときたま、コーディングをしていると、新しい機能を既存構造に追加するのが非常に難しくなっていることに気づくかもしれません。

それはやばいコードの匂いがするので、我々は作業を止めて、すぐにリファクタリングすべきです。

BackgroundLayer クラスのリファクタリング

我々は Chipmunk 物理ボディを我々の背景に追加するので、PlayScene で作成された空間オブジェクトを獲得するためのメソッドを必要とします。

Background レイヤにある ctor 関数の名前を変更して、それに space と名付けられたパラメータを渡して下さい。我々はまた、BackgroundLayer クラスに新しいメンバ変数も追加すべきです。ここにコード片を置きます:

    ctor:function (space) {
        this._super();

        // ここで古い配列をクリア
        this.objects = [];
        this.space = space;

        this.init();
    },

ここで我々は追加の init コードを追加しました。我々は objects と名付けられた配列を追加し、それを空の配列に初期化します。

(★注記:あなたは、 this.space = space という代入の後、this.init() メソッドを呼ぶべきです。なぜなら我々は物理オブジェクトを init メソッドのなかで作るためです。★)

ヘルパーメソッドの追加

  1. BackgroundLayer クラスにもう少しメンバ変数を追加して下さい:
    space:null,
    spriteSheet:null,
    objects:[],
    
  2. init メソッドの中のスプライトシートを初期化して下さい:
    // スプライトシートの作成
        cc.spriteFrameCache.addSpriteFrames(res.background_plist);
        this.spriteSheet = new cc.SpriteBatchNode(res.background_png);
        this.addChild(this.spriteSheet);
    
  3. 岩とコイン初期化するために loadObject と名付けられたメソッドを追加
    loadObjects:function (map, mapIndex) {
        // コインの追加
        var coinGroup = map.getObjectGroup("coin");
        var coinArray = coinGroup.getObjects();
        for (var i = 0; i < coinArray.length; i++) {
            var coin = new Coin(this.spriteSheet,
                this.space,
                cc.p(coinArray[i]["x"] + this.mapWidth * mapIndex,coinArray[i]["y"]));
            coin.mapIndex = mapIndex;
            this.objects.push(coin);
        }
    
        // 岩の追加
        var rockGroup = map.getObjectGroup("rock");
        var rockArray = rockGroup.getObjects();
        for (var i = 0; i < rockArray.length; i++) {
            var rock = new Rock(this.spriteSheet,
                this.space,
                rockArray[i]["x"] + this.mapWidth * mapIndex);
            rock.mapIndex = mapIndex;
            this.objects.push(rock);
        }
    },
    

ここで我々はタイルドマップ中の全てのオブジェクト情報に繰り返し適用し、対応する Chipmunk リジッドボディを作成します。最後に我々はこれらのオブジェクトを objects 配列に格納します。

これらのコードの全ては、自己説明的です。あなたは mapIndex パラメータに注意を払うのみにすべきです。我々は、リジッドボディをどこに配置すべきか計算するためにパラメータを使います。

我々は、最初の2つのスクリーンマップにおける物理オブジェクトを作成するために init メソッドの最後で loadObject メソッドを呼び出す必要があります。

this.loadObjects(this.map00, 0);
this.loadObjects(this.map01, 1);
  1. その他の2つのヘルパーメソッドを使われていない chipmunk リジッドボディを削除するために追加して下さい。

最初のメソッドは、removeObjects と呼ばれます。それはオブジェクトを mapIndex によって削除します。ここに実装があります:

removeObjects:function (mapIndex) {
        while((function (obj, index) {
            for (var i = 0; i < obj.length; i++) {
                if (obj[i].mapIndex == index) {
                    obj[i].removeFromParent();
                    obj.splice(i, 1);
                    return true;
                }
            }
            return false;
        })(this.objects, mapIndex));
    },

他のメソッドは、removeObjectByShape と呼ばれます:

   removeObjectByShape:function (shape) {
        for (var i = 0; i < this.objects.length; i++) {
            if (this.objects[i].getShape() == shape) {
                this.objects[i].removeFromParent();
                this.objects.splice(i, 1);
                break;
            }
        }
    },

このメソッドは、chipmunk オブジェクトをそのシェイプによって削除します。

ラップアップ:checkAndReload メソッドにおける作成と使い捨てロジックの追加

マップが動かされたら、我々はまた、"コイン&岩"を再作成するために loadObject メソッドも呼び出すべきです。

更にまた、我々は removeObjects メソッドを呼び出すことで使われていないオブジェクトの全て削除すべきです。

ここにコード片を置いておきます:

  checkAndReload:function (eyeX) {
        var newMapIndex = parseInt(eyeX / this.mapWidth);
        if (this.mapIndex == newMapIndex) {
            return false;
        }

        if (0 == newMapIndex % 2) {
            // mapSecond の変更
            this.map01.setPositionX(this.mapWidth * (newMapIndex + 1));
            this.loadObjects(this.map01, newMapIndex + 1);
        } else {
            // mapFirst の変更
            this.map00.setPositionX(this.mapWidth * (newMapIndex + 1));
            this.loadObjects(this.map00, newMapIndex + 1);
        }
        this.removeObjects(newMapIndex - 1);
        this.mapIndex = newMapIndex;

        return true;
    },

コインと岩の追加

これでコインと岩の実装を追加するところまで来ました。実装の詳細に関わらず、あなたはまたこれらの2つのクラスの背後にあるデザインの考え方に注意を払うべきでもあります。ここで我々は、cc.Sprite.の代わりに cc.Class から継承することにします。我々はそれぞれのオブジェクトが cc.Sprite のインスタンスを所有するようにします。

Coin クラスのデザインと実装

  1. coin.js と名付けられた新しいファイルを作成して下さい。我々は我々の Coin クラスをこのファイルの中で定義します。注意して欲しいのは、あなたはこのファイルが、あなたの src ディレクトリに置かれるようにする、ということです。
  2. cc.Class から Coin と名付けられたクラスを派生させ、実装全体を概観してみましょう:
    var Coin = cc.Class.extend({
    space:null,
    sprite:null,
    shape:null,
    _mapIndex:0,// どのマップに属するか
    get mapIndex() {
        return this._mapIndex;
    },
    set mapIndex(index) {
        this._mapIndex = index;
    },
    
    /** Constructor
     * @param {cc.SpriteBatchNode *}
     * @param {cp.Space *}
     * @param {cc.p}
     */
    ctor:function (spriteSheet, space, pos) {
        this.space = space;
    
        // コインアニメーションの初期化
        var animFrames = [];
        for (var i = 0; i < 8; i++) {
            var str = "coin" + i + ".png";
            var frame = cc.spriteFrameCache.getSpriteFrame(str);
            animFrames.push(frame);
        }
    
        var animation = new cc.Animation(animFrames, 0.2);
        var action = new cc.RepeatForever(new cc.Animate(animation));
    
        this.sprite = new cc.PhysicsSprite("#coin0.png");
    
        // 物理の初期化
        var radius = 0.95 * this.sprite.getContentSize().width / 2;
        var body = new cp.StaticBody();
        body.setPos(pos);
        this.sprite.setBody(body);
    
        this.shape = new cp.CircleShape(body, radius, cp.vzero);
        this.shape.setCollisionType(SpriteTag.coin);
        //センサは衝突コールバックを呼ぶだけで、実際の衝突は一切生成しません
    
        this.shape.setSensor(true);
    
        this.space.addStaticShape(this.shape);
    
        // スプライトをスプライトシートに追加
        this.sprite.runAction(action);
        spriteSheet.addChild(this.sprite, 1);
    },
    
    removeFromParent:function () {
        this.space.removeStaticShape(this.shape);
        this.shape = null;
        this.sprite.removeFromParent();
        this.sprite = null;
    },
    
    getShape:function () {
        return this.shape;
    }
    });
    

一つ一つコードを説明していきましょう。

まず最初に、我々は space, sprite そして shape と名付けられた3つのメンバ変数を追加します。我々はこれらの変数を、コインオブジェクトの物理ボディとその表示属性を作成するために使います。

それから、我々は他にメンバ変数 _mapIndex を追加しました。我々は変数へのアクセサを定義するために get/set シンタックスシュガー(糖衣構文)を使いました。

ctor メソッドは、Coin クラスのコンストラクタです。我々はCoin クラスをスプライトシート、空間、そして位置オブジェクトで後で作成します。

コインは丸い形をしているので、我々はリジッドボディをアタッチされた CircleShape を作成します。ctor 関数の残りの部分は、自己説明的です。

最後に、我々はクリーンアップ作業をするためのメソッドを定義する必要があります。それは removeFromParent メソッドです。それは最初にリジッドボディを空間から削除し、それからその親からスプライトを削除します。getShape メソッドは、コインオブジェクト中に格納されているシェイプオブジェクトにアクセスするために使われるゲッタメソッドです。

Rock クラスのデザインと実装

Rock クラスのデザインの原則は、リジッドシェイプタイプパート(訳注:?)を除いた Coin クラスと多かれ少なかれ同じです。

なぜなら我々の Rock クラスは矩形のボックスだからです。そのため我々は、Coin クラス中の cc.CircleShape を置き換えるために cp.BoxShape を使います。

ここに rock.js のフルソースコードを置いておきます:

var Rock = cc.Class.extend({
    space:null,
    sprite:null,
    shape:null,
    _map:0,// どのマップに属するか
    get map() {
        return this._map;
    },
    set map(newMap) {
        this._map = newMap;
    },

    /** Constructor
     * @param {cc.SpriteBatchNode *}
     * @param {cp.Space *}
     * @param {cc.p}
     */
    ctor:function (spriteSheet, space, posX) {
        this.space = space;

        this.sprite = new cc.PhysicsSprite("#rock.png");
        var body = new cp.StaticBody();
        body.setPos(cc.p(posX, this.sprite.getContentSize().height / 2 + g_groundHeight));
        this.sprite.setBody(body);

        this.shape = new cp.BoxShape(body,
            this.sprite.getContentSize().width,
            this.sprite.getContentSize().height);
        this.shape.setCollisionType(SpriteTag.rock);

        this.space.addStaticShape(this.shape);
        spriteSheet.addChild(this.sprite);
    },

    removeFromParent:function () {
        this.space.removeStaticShape(this.shape);
        this.shape = null;
        this.sprite.removeFromParent();
        this.sprite = null;
    },

    getShape:function () {
        return this.shape;
    }
});

PlayScene の改良

PlayScene の onEnter 関数のリファクタリング

  • まず最初に、shapesToRemove と名付けられた追加の配列を追加し、PlayScene.js 中の onEnter 関数の最初でそれを初期化しましょう。
    //以下の行を init のメンバ変数中にarea
    shapesToRemove :[] を定義するために置き、
    
    //以下の行を★onEnter★関数の最初に置きます。
    this.shapesToRemove = [];
    
  • 2番目に、BackgroundLayer の作成を変更します。ここで我々は BackgroundLayer のコンストラクタ中に空間オブジェクトを単純に渡します。 this.gameLayer.addChild(new BackgroundLayer(this.space), 0, TagOfLayer.background);

衝突検出コールバックの追加

  • まず最初に、我々は initPhyiscs メソッドの最後でこれらの2つの関数を呼びべきです:
     // chipmunk の CollisionHandler のセットアップ
            this.space.addCollisionHandler(SpriteTag.runner, SpriteTag.coin,
                this.collisionCoinBegin.bind(this), null, null, null);
            this.space.addCollisionHandler(SpriteTag.runner, SpriteTag.rock,
                this.collisionRockBegin.bind(this), null, null, null);
    

addCollisionHandler メソッドは、衝突が発生した時のコールバックを必要とします。

  • それでは、プレイヤーがコインと岩に衝突するのをハンドリングするための、これらの2つのコールバックを定義しましょう。
     collisionCoinBegin:function (arbiter, space) {
            var shapes = arbiter.getShapes();
            // shapes[0] はランナー
            this.shapesToRemove.push(shapes[1]);
        },
    
        collisionRockBegin:function (arbiter, space) {
            cc.log("==game over");
        },
    
  • 背景レイヤにある使われていないリジッドボディは削除して下さい。あなたは以下のコードを update メソッドの最後に追加すべきです:
            // cpSpaceAddPostStepCallback のシミュレーション
            for(var i = 0; i < this.shapesToRemove.length; i++) {
                var shape = this.shapesToRemove[i];
                this.gameLayer.getChildByTag(TagOfLayer.background).removeObjectByShape(shape);
            }
            this.shapesToRemove = [];
    

我々は物理シミュレーションプロセス中は物理ボディを削除できません。そのため我々は、削除される必要のある一時的なデータを把握するための shapesToRemove と名付けられた追加の配列を使います。

以上全てのことのラップアップ

おめでとうございます!あなたはほとんど最後まで到達しました。結果を見るために debug ボタンを叩く前に、全てのものを一緒に接続するための追加のグルーコードを追加しましょう。

project.json を開いて、jsList 配列の最後にもう2つ配列項目を追記して下さい。

    "jsList" : [
        "src/resource.js",
        "src/app.js",
        "src/AnimationLayer.js",
        "src/BackgroundLayer.js",
        "src/PlayScene.js",
        "src/StatusLayer.js",
        "src/globals.js",
        "src/coin.js",
        "src/rock.js"
    ]

ビルドして実行!おめでとう、やりました!:)

我々の最終的な成果を見てみましょう:

要約

このチュートリアルでは、我々は非常に長い旅を楽しみました。しかし、価値あるものでした、そうでしょう?

我々は、複雑なゲームレベルをデザインするための TiledMap のオブジェクトレイヤの使い方と、あなたのコードの構造を拡張するためのあなた自身のクラスのカスタマイズ方法を学びました。

あなたはここからフルソースコードをダウンロードできます。

ここから進むのは?

次のチュートリアルでは、我々はゲームの HUD を一定して更新する方法をカバーし、更に我々はまた、ゲームオーバーロジックと、プレイヤーに障害物の上をジャンプさせるようにするために我々のゲームの中にシンプルなジェスチャレコグナイザも追加します。このまま進みましょう!

posted by cbbandtqb at 23:12| Comment(0) | TrackBack(0) | 備忘録 | このブログの読者になる | 更新情報をチェックする

2015年10月18日

Cocos2d-JS > EXPLORER WITH TILEDMAP AND DISPLAY

原文はこちらです。

イントロダクション

このチュートリアルでは、新しい背景として TiledMap をパルクールゲームに追加する方法を示します。

我々はまた、背景を無限にスクロールさせ、そしてプレイヤーを無限に走らせる方法も学びます。

これらの背後にある魔法は、cocos2d のレイヤを動かし方が全てです。

準備すべき幾つかの事柄

我々が我々の手を汚す前に、リソースファイルと我々のゲームに一致する名前を追加しましょう。

リソースとグローバルのセットアップ

我々は他のレイヤをそれぞれのレイヤ内で参照する必要があります。そのため、レイヤを取り出す最も良い方法は、タグ経由とすることです。

globals.js に以下のコードを追加して下さい:

if(typeof TagOfLayer == "undefined") {
    var TagOfLayer = {};
    TagOfLayer.background = 0;
    TagOfLayer.Animation = 1;
    TagOfLayer.Status = 2;
};

ここで我々は背景レイヤ、アニメーションレイヤ、そして状態レイヤにタグ名を与えており、そうすることで我々は他のレイヤをタグによって取り出すことができます。

我々はまた、resource.js 内のリソース変数を追加する必要があります:(訳注:コメントの内容とコードの内容が一致していない?)

//我々の2つのタイルドマップは s_map00 及び s_map01 と命名されています。
var res = {
    helloBG_png : "res/helloBG.png",
    start_n_png : "res/start_n.png",
    start_s_png : "res/start_s.png",
    PlayBG_png  : "res/PlayBG.png",
    runner_png  : "res/running.png",
    runner_plist : "res/running.plist",
    map_png: "res/map.png",
    map00_tmx: "res/map00.tmx",
    map01_tmx: "res/map01.tmx"
};

var g_resources = [
    //画像
    res.helloBG_png,
    res.start_n_png,
    res.start_s_png,
    res.PlayBG_png,
    res.runner_png,
    res.runner_plist,
    res.map_png,
    res.map00_tmx,
    res.map01_tmx
];

上記のコードは自己説明的なので、次のセクションに進みましょう。

Chipmunk のデバッグ描画の有効化

もし我々が Chipmunk 物理に取り組んでいるなら、あなたはデバッグ描画を有効化した方が良いでしょう。そうすることで、デバッグプロセスがより扱い易くなるでしょう。

以下のコードを AnimationLayer.js の ctor 関数に追加して下さい:

this._debugNode = new cc.PhysicsDebugNode(this.space);
// パララックス率とオフセット
this.addChild(this._debugNode, 10);

あなたがゲームを再び実行すると、あなたは走るプレイヤーの上に赤い箱を見ることになるでしょう:

TiledMap の導入

TiledMap は2dゲームで非常によく使われる概念です。それは広大なレベルマップやパララックススクロールする背景を構築するのに役立ちます。

TiledMap は通常の PNG ファイルよりメモリを少な目に消費します。もしあなたが非常に広大なレベルマップを構築したいなら、それは絶対的にあなたの正しい選択になります。

面倒なことは置いておいて、TiledMap にダイブしましょう。

TiledMap による背景のデザインと作成

まず最初に、あなたは TiledMap をダウンロードすべきです。あなたはそれをここからダウンロードできます。TiledMap はクロスプラットフォームソフトウェアであり、多くの異なるバージョンの種類があります。あなたはあなたのオペレーティングシステムによってバージョンを選ぶことができます。Tiled エディタをダウンロード後、あなたはその利用方法に精通すべきです。あなたはそのドキュメントを見てみたくなるかもしれません。

あなたが Tiled を快適に感じたら、あなたは我々が提供したタイルセットであなたのタイルドマップをデザインすることができます。

2つのタイルドマップを作成する詳細なプロセスは、このチュートリアルのスコープ外です。

(★注記:もしあなたが自身でタイルドマップを作成できないなら、あなたはプロセスを安全にスキップすることができ、我々によって提供されるタイルドマップを使うことができます。★)

以前の背景をタイルドマップで置き換え

それでは、古い静的な背景画像を、我々の新しく素晴らしいタイルドマップに置き換えましょう。

我々はこのことを BackgroundLayer.js にて行います。まず最初に、我々は BackgroundLayer クラスにて4つのメンバ変数を追加すべきです:

map00:null,
map01:null,
mapWidth:0,
mapIndex:0,

我々は、静的な背景を作成するために我々が必要とした古いコードを削除すべきです。

(★注記★:ここではコード片にコメントしませんが、あなたは安全にそれらの全てを削除することができます。)

//        var winSize = cc.Director.getInstance().getWinSize();
//
//        var centerPos = cc.p(winSize.width / 2, winSize.height / 2);
//        var spriteBG = new cc.Sprite(s_PlayBG);
//        spriteBG.setPosition(centerPos);
//        this.addChild(spriteBG);

最後に、我々は新しいコード片をタイルドマップの背景を作成するために追加します。

this.map00 = new cc.TMXTiledMap(res.map00_tmx);
this.addChild(this.map00);
this.mapWidth = this.map00.getContentSize().width;
this.map01 = new cc.TMXTiledMap(res.map01_tmx);
this.map01.setPosition(cc.p(this.mapWidth, 0));
this.addChild(this.map01);

全ての変更を保存し、実行して下さい:

ここで、我々は2つのマップを追加します。map01 は、map00 による背景の真横になります。以降のセクションにて、我々はなぜ2つのマップを追加すべきなのかを説明します。

シーンディスプレイの導入

物理ボディは無限に右に動くので、スプライトは物理ボディと共にその位置を同期していきます。

数秒後、プレイヤーはスクリーンの外へ出てしまい、ちょうど直前のチュートリアルでなったようになります。

そこで我々はゲームレイヤの x ポジションを、見える範囲にそれぞれのフレームが収まるように、動かす必要があります。ここに AnimationLayer.js のコード片を置いておきます:

getEyeX:function () {
    return this.sprite.getPositionX() - g_runnerStartX;
},

ここで getEyeX 関数は、アニメーションレイヤのデルタ運動を計算します。

我々は this.gameLayer の同じだけのデルタ運動分を動かし、それは背景レイヤとアニメーションレイヤを反対方向にて含み、そして我々は update メソッドをフレーム毎に、以下のコードを PlayScene.js の update メソッドの最後に追加することによって、呼び出すことができるでしょう:

     update:function (dt) {
        // chipmunk のステップ
        this.space.step(dt);

        var animationLayer = this.gameLayer.getChildByTag(TagOfLayer.Animation);
        var eyeX = animationLayer.getEyeX();

        this.gameLayer.setPosition(cc.p(-eyeX,0));
    }

背景レイヤの移動

背景レイヤの移動をセットアップするためのプロセスは、最後のセクションで行ってきたこととほとんど同じです。しかし、我々はは2つのタイルドマップについて少し計算を行う必要があります。

それでは進めましょう。新しいメンバ関数 checkAndReload を BackgroundLayer に追加して下さい:

    checkAndReload:function (eyeX) {
        var newMapIndex = parseInt(eyeX / this.mapWidth);
        if (this.mapIndex == newMapIndex) {
            return false;
        }
        if (0 == newMapIndex % 2) {
            // mapSecond の変更
            this.map01.setPositionX(this.mapWidth * (newMapIndex + 1));
        } else {
            // mapFirst の変更
            this.map00.setPositionX(this.mapWidth * (newMapIndex + 1));
        }
        this.mapIndex = newMapIndex;
        return true;
    },

eyeX がスクリーンの幅に達すると、式 parseInt(eyeX / this.mapWidth) はゼロより大きな値を取ります。

我々は、どちらのマップが動く必要があるのか、何ピクセル動く必要があるのかを決定するために newMapIndex を使います。

それから我々はこの関数をフレーム毎に呼び出すべきです。

    update:function (dt) {
        var animationLayer = this.getParent().getChildByTag(TagOfLayer.Animation);
        var eyeX = animationLayer.getEyeX();
        this.checkAndReload(eyeX);
    }

最後に、我々は背景レイヤの init メソッドの最後で scheduleUpdate を呼ぶべきです:

 this.scheduleUpdate();

ラップアップ

どうでしょうか。我々は幾つか最後の終了作業を行うべきです。

レイヤのタグを追加するために PlayScene の onEnter メソッドを変更し、背景レイヤとアニメーションをゲームレイヤに追加して下さい:

    onEnter:function () {
        this._super();
        this.initPhysics();
        this.gameLayer = new cc.Layer();

        //背景レイヤとアニメーションレイヤをゲームレイヤに追加
        this.gameLayer.addChild(new BackgroundLayer(), 0, TagOfLayer.background);
        this.gameLayer.addChild(new AnimationLayer(this.space), 0, TagOfLayer.Animation);
        this.addChild(this.gameLayer);
        this.addChild(new StatusLayer(), 0, TagOfLayer.Status);

        this.scheduleUpdate();
    },

やりました!あなたは無事にこのチュートリアルを終了しました。実行して見てみましょう。

注記:もしあなたが chipmunk のリジッドボディのデバッグ描画情報を表示したくなければ、あなたは PhysicsDebugNode の生成後に以下のコードを安全に追加することができます:

this._debugNode.setVisible(false);

要約

このチュートリアルでは、我々は TiledMap とその表示を扱いました。これらの2つのコンセプトは、あなたが物理的にエンドレスで走るゲームを開発するときには、非常に重要なものです。

あなたはここからプロジェクト全体をダウンロードすることができます。

ここから進むのは?

次のチュートリアルでは、我々はコインと障害物を我々のゲームに追加します。またチュートリアルにて、我々は我々のゲームのリファクタリングと、より拡張できるようにする方法も学びます。

我々はまた PlayScene にて幾つかのクリーンアップ作業を行い、Coin 及び Rock と命名された2つのクラスをカプセル化します。

次のチュートリアルに進みましょう!

posted by cbbandtqb at 20:39| Comment(0) | TrackBack(0) | 備忘録 | このブログの読者になる | 更新情報をチェックする

2015年10月15日

Cocos2d-JS > ADD CHIPMUNK PHYSIC ENGINE TO OUR GAME

原文はこちらです。

イントロダクション

Cocos2d-JS は、我々に印象的なゲームワールドを作成するパワーを与えることができます。しかし若干リアルさに欠けています。 我々はゲームワールドがよりリアルになるように複雑な計算をさせることもできますが、我々のライフを容易にできるような別のオプションがあります。答えは物理エンジンです。

物理エンジンは重力、衝突判定、そして我々のゲームワールドをよりリアルに見せる物理シミュレーションを提供します。

このチュートリアルでは、我々は Chipmunk 物理エンジンを我々のパルクールゲームに導入しようと思います。

なぜ Chipmunk 物理エンジン?

なぜ我々は Chipmunk 物理エンジンを選ぶべきなのでしょうか?なぜならそれは我々に他の2D物理エンジンより更に強力なパワーを与えてくれるからです。

Chipmunk 物理エンジンでなければ、もう1つのオプションがあります - Box2D です。

Box2D は素晴らしい物理エンジンで、非常に長期間存在し続けています。多くの2dゲームがゲーム物理のために Box2D を使い続けています。

しかし Chipmunk はそれ自身のアドバンテージを持っています。あなたはより多くの情報のために Chipmunk のウェブサイトに行くことができます。

Chipmunk 物理エンジンを Cocos2d-JS で利用可能に

準備

まず最初に、 Chipmunk を Cocos2d-JS で利用可能にします。

project.json ファイルを開いて、以下を変更します:

 "modules" : ["cocos2d"],

以下に変更します:

 "modules" : ["cocos2d","chipmunk"],

そうすることで、Cocos2d-JS が起動し終えると、Chipmunk ライブラリを自動的にロードします。

次に、globals.js と名付けられた新しいファイルを作成し、その中に2つのグローバル変数を追加して下さい。

var g_groundHeight = 57;
var g_runnerStartX = 80;

最後に、我々は、エンジンが起動した時に globals.js ファイルをロードすることをフレームワークに伝えるべきです。

globals.js のパスを jsList 配列の最後に追記して下さい:

    "jsList" : [
        "src/resource.js",
        "src/app.js",
        "src/AnimationLayer.js",
        "src/BackgroundLayer.js",
        "src/PlayScene.js",
        "src/StatusLayer.js",
        "src/globals.js"

    ]

注記:あなたが新しいファイルを Cocos2d-JS に追加する時はいつでも、あなたはそれを jsList 配列に追加することを思い出すべきです。

Chipmunk 物理ワールドの初期化

Chipmunk では、物理ワールドを表現するための space オブジェクトがあります。

まず最初に、PlayScene.js ファイル中に space と名付けられた新しいメンバ変数を追加して下さい:

space:null,

概して、1つのゲームは1つの space オブジェクトだけを必要とします。空間オブジェクトは異なるレイヤによって共有され得ます。我々は通常、space 初期化コードを PlayScene に置きます。

ここに物理ワールドをセットアップするためのコードを置きます:

    // chipmunk の space を初期化
    initPhysics:function() {
        //1. 新しい space オブジェクト
        this.space = new cp.Space();
        //2. 重力のセットアップ
        this.space.gravity = cp.v(0, -350);

        // 3.壁のセットアップ
        var wallBottom = new cp.SegmentShape(this.space.staticBody,
            cp.v(0, g_groundHeight),// 開始地点
            cp.v(4294967295, g_groundHeight),// MAX INT:4294967295
            0);// 壁の厚さ
        this.space.addStaticShape(wallBottom);
    },

上記のコードは自己説明的になっているので、我々は 安全に削除することができます。もしあなたがこれらの API の詳細について知りたければ、あなたはより多くの情報のために Chipmunk の公式ドキュメントを参照すべきです。

次に、我々のゲームのメインループを定義します:

    update:function (dt) {
        // chipmunk のステップ
        this.space.step(dt);
    }

update 関数にて、我々は Chipmunk に物理のシミュレーションを開始するように伝えます。

我々がより深く進む前に、AnimationLayer に小さな変更を加えましょう。我々は物理アクタを AnimationLayer に作成するので、space オブジェクトを渡す AnimationLayer のコンストラクタを変更すべきです。

ctor:function (space) {
        this._super();
        this.space = space;
        this.init();
    },

もちろん、我々は AnimationLayer にて弱参照のメンバ変数を定義すべきで、null で初期化すべきです。

そのようにすることで、我々の準備は整いました。終わりまで完成させて、onEnter 関数にてこれらのメソッドを呼んでみましょう:

    onEnter:function () {
        this._super();
        this.initPhysics();

        this.addChild(new BackgroundLayer());
        this.addChild(new AnimationLayer(this.space));
        this.addChild(new StatusLayer());

        this.scheduleUpdate();
    },

注記

あなたは物理空間を初期化し、それを AnimationLayer に渡すべきです。

物理コンポーネントをランナースプライトに追加

直前のチュートリアルにて、我々はスプライトシートを使ってランナーを作成しました。このセクションでは、我々は PhysicsSprite を使ってランナーを書き直します。

PhysicsSprite は再利用可能なコンポーネントであり、物理ボディと cocos2d のスプライトを結合できます。

ここにランナーを PhysicsSprite で作成するためのコードを置きます:

        //1.PhysicsSprite をスプライトのフレーム名で作成
        this.sprite = new cc.PhysicsSprite("#runner0.png");
        var contentSize = this.sprite.getContentSize();
        // 2. ランナーを物理ボディで初期化
        this.body = new cp.Body(1, cp.momentForBox(1, contentSize.width, contentSize.height));
        //3. ランナーの位置を設定
        this.body.p = cc.p(g_runnerStartX, g_groundHeight + contentSize.height / 2);
        //4. ボディにインパルスを追加
        this.body.applyImpulse(cp.v(150, 0), cp.v(0, 0));//run speed
走るスピード
        //5. 作成されたボディを空間に追加
        this.space.addBody(this.body);
        //6.ボディのためにシェイプを作成
        this.shape = new cp.BoxShape(this.body, contentSize.width - 14, contentSize.height);
        //7. シェイプを空間に追加
        this.space.addShape(this.shape);
        //8.ボディを物理スプライトに設定
        this.sprite.setBody(this.body);

コードとコメントは自己説明的です。これらのコードを AnimationLayerinit メソッドに追加して下さい。

デバッグ及びテスト

おめでとうございます。あなたは全てのボルトとナットを締めました。あなたは Webstorm の中で debug ボタンを押すことができます。

これであなたはランナーがスクリーンを横切って走るのを見ることができます。

要約

このチュートリアルでは、我々は Chipmunk 物理ワールドのセットアップ方法、物理ワールド境界のセットアップ方法、確固としたボディと関連付けられたシェイプの作成方法を示しました。我々はまた、スプライトに更にリアルに振る舞わせるように物理を追加しました。あなたはここからソースコード全体を得ることができます。

ここから進むのは

次のチュートリアルでは、我々はカメラの動きをゲームに導入します。そして背景画像を tiledMap に置き換えます。
更に重要なこととして、我々は背景をゲームに表示される無限ループにします。次のチュートリアルに進みましょう。

posted by cbbandtqb at 01:09| Comment(0) | TrackBack(0) | 備忘録 | このブログの読者になる | 更新情報をチェックする