【Part8】スマホで作るPhaserゲーム〜チュートリアル編〜

ゲーム

どうもです。タドスケです。

前回はプレイヤーを配置してタッチで操作できるところまで作りました。

今回はゲームらしい要素として、⭐️を登場させたいと思います。




動かしてみる

いつものように動かすところから始めましょう。

コード量が多くなってきたので、今回は整理したコード全文を載せます。

以下のコードを丸ごとJSタブに上書きコピーしてください。(preload内のパスは適宜置き換えてください)

//---------------------------
// グローバル変数
//---------------------------
// ゲーム設定
var config = {
    type: Phaser.AUTO,
    width: 300,
    height: 400,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};
// ゲーム本体
var game = new Phaser.Game(config);
// プレイヤー
var player;
// 足場グループ
var platforms;
// 星グループ
var stars;

//---------------------------
// ロード
//---------------------------
function preload ()
{
    this.load.image('star', 'img/20210425182551.png');
    this.load.image('sky', 'img/20210425175550.png');
    this.load.image('ground', 'img/20210425175525.png');
    this.load.spritesheet('dude', 
        'img/20210425182602.png',
        { frameWidth: 32, frameHeight: 48 }
    );
}

//---------------------------
// 足場の生成
//---------------------------
function createPlatforms(scene)
{
    // 足場グループを生成
    platforms = scene.physics.add.staticGroup();

    // 足場1
    platforms.create(300, 150, 'ground').setScale(0.5).refreshBody();

    // 足場2
    platforms.create(0, 250, 'ground').setScale(0.5).refreshBody();

    // 地面
    platforms.create(150, 385, 'ground');
}

//---------------------------
// プレイヤーの生成
//---------------------------
function createPlayer(scene)
{
    // プレイヤー
    player = scene.physics.add.sprite(150, 200, 'dude');
    player.setBounce(0.2);
    player.setCollideWorldBounds(true);

    // プレイヤーアニメーション:正面
    scene.anims.create({
        key: 'turn',
        frameRate: 20,
        frames: 
            [{ key: 'dude', frame: 4 }],
    });

    // プレイヤーアニメーション:左
    scene.anims.create({
        key: 'left',
        frameRate: 10,
        repeat: -1,
        frames:         
              scene.anims.generateFrameNumbers(
                'dude', 
                { start: 0, end: 3 }
            ),
    });
 
    // プレイヤーアニメーション:右
    scene.anims.create({
        key: 'right',
        frameRate: 10,
        repeat: -1,
        frames: 
            scene.anims.generateFrameNumbers(
                'dude',
                { start: 5, end: 8 }
            ),
    });
}

//---------------------------
// 星の生成
//---------------------------
function createStars(scene)
{
    // グループの生成
    stars = scene.physics.add.group({
        key: 'star',
        repeat: 5,
        setXY: { x: 13, y: 0, stepX: 68 }
    });

    // 星の物理挙動を設定
    stars.children.iterate(function (child) {
        child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
    });
}

//---------------------------
// 衝突の設定
//---------------------------
function colliderSetting(scene)
{
    // 星と足場
    scene.physics.add.collider(stars, platforms);

    // プレイヤーと星
    scene.physics.add.overlap(player, stars, collectStar, null, this);

    // プレイヤーと足場
    scene.physics.add.collider(player, platforms);
}

//---------------------------
// プレイヤーと星の衝突時に呼ばれる
//---------------------------
function collectStar (player, star)
{
    // 星を消す
    star.disableBody(true, true);
}

//---------------------------
// ロード後処理
//---------------------------
function create ()
{
    // this = scene

    // マウス入力を有効にする
    this.input.mouse.capture = true

    // 背景
    this.add.image(300, 300, 'sky');

    // 足場
    createPlatforms(this);

    // プレイヤー
    createPlayer(this);

    // 星
    createStars(this);

    // 衝突の設定
    colliderSetting(this);
}

//---------------------------
// 更新処理
//---------------------------
function update ()
{
    var pointer = this.input.activePointer;
    if(pointer.isDown)
    {
        // 左移動
        if(pointer.x < 150)
        {
            player.setVelocityX(-160);
            player.anims.play('left',true);
        }
        // 右移動
        else if(150 < pointer.x)
        {
            player.setVelocityX(160);
            player.anims.play('right',true);
        }

        // ジャンプ
        if(pointer.y < 200 && player.body.touching.down)
        {
            player.setVelocityY(-330);
        }
    }
    // 停止
    else
    {
        player.setVelocityX(0);
        player.anims.play('turn');
    }
}

実行すると⭐️が降ってきて、プレイヤーが触れると消えます。

コード解説

コード整理

まず最初にやったこととして、コード全体を見やすく整理しました。

create以下にある各生成処理を個別の関数に分けています。

一つ注意しないといけないのは、thisは関数の呼び出し元になるということです。

create関数内では、呼び出し元は関数を登録したSceneなので、this=Sceneとなります。

しかしcreate関数内から別の関数を呼び出すと、this≠Sceneになってしまうので、create関数内のコードをコピペしただけだとthis部分でエラーになってしまいます。

これを回避するために、create関数から呼ぶ関数の引数にthisを渡して、呼び出し先でsceneとして参照できるようにしています。

⭐️の生成

⭐️はcreateStars関数内で生成しています。

グループの生成

stars = scene.physics.add.group({
    key: 'star',
    repeat: 5,
    setXY: { x: 13, y: 0, stepX: 68 }
});

足場の時はグループを生成→createで一個ずつ作っていましたが、この処理ではまとめて生成しています。

パラメーターの指定方法を見てみると、アニメーションの一括登録に近いですね。

key画像の名前
repeat繰り返し数
setXYx,y:開始座標
stepX:xの増加量

開始点から一定間隔で星を生成する処理になっています。

物理挙動の設定

stars.children.iterate(function (child) {    
    child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
    });

こちらも複数の⭐️に対して一括で設定するための処理です。

iterate(function(child)) で、⭐️グループ以下の全ての子に対してそれぞれ関数を呼ぶことができます。

引数のchildには⭐️が一個ずつ入ります。

関数内で⭐️の弾み具合を指定しています。

Math.FloatBetween は2つの値の間の小数をランダムで得られる関数です。

これにより⭐️の弾み具合がランダムになります。

衝突判定

衝突判定は colliderSetting 関数にまとめました。

// プレイヤーと星
scene.physics.add.overlap(player, stars, collectStar, null, this);

// プレイヤーと足場
scene.physics.add.collider(player, platforms);

⭐️と足場の設定はプレイヤーと同じですが、プレイヤーと⭐️の設定はこれまでと違いますね。

overlap は足場のように乗ったりはできないけど重なった時に指定の関数を呼ぶようにする処理です。

プレイヤーが⭐️に触れると collectStar 関数が呼ばれるので、その中で⭐️を消しています。

まとめ

今回は⭐️を追加してプレイヤーが触れると消えるところまで作りました。

次回はスコア表示を作ります!

コメント

  1. […] どうもです。タドスケです。 Part2ではPhaserを起動するところまで進めました。 Part3, Part4ではゲームのステージを作っていきます。 同じコードを利用するので、2回分いっぺんにやってしまいま… 【Part6】スマホで作るPhaserゲーム〜チュートリアル編〜 【Part8】スマホで作るPhaserゲーム〜チュートリアル編〜 […]

  2. […] スマホアプリ:JS Anywhereゲームライブラリ:Phaser … 【Part8】スマホで作るPhaserゲーム〜チュートリアル編〜 […]

  3. […] […]

タイトルとURLをコピーしました