😎

phaserでゲーム開発 第二回

に公開

phaserでゲーム開発シリーズ、第二回。
今回は画面に足場を配置してみたいと思います。
前回の記事はこちら。

https://zenn.dev/k_tabuchi/articles/2dc9016a5a5642

まずは、コードの全文です。

GameScene.js
/**
 * ゲームのメインシーンを表すクラス
 * 
 * @extends Phaser.Scene
 */
export class GameScene extends Phaser.Scene {
    /**
     * シーンのコンストラクタ
     * - シーンキーを "GameScene" として登録する
     */
    constructor() {
        super("GameScene");
    }

    /**
     * アセットの事前読み込みを行う
     * - 画像や音声などをロードする
     * 
     * @override
     */
    preload() {
        this.load.setBaseURL('/assets/');
        this.load.image('sky', 'img/bg7.jpg');
        this.load.spritesheet('ground', 'img/map_chip/base.png', {
            frameWidth: 16,
            frameHeight: 16
        });
    }

    /**
     * シーン初期化時に呼ばれる
     * - 背景と足場を作成する
     * 
     * @override
     */
    create() {
        this.createBackground();
        this.createPlatforms();
    }

    /**
     * 背景を作成してゲーム全体に配置する
     */
    createBackground() {
        const bg = this.add.image(0, 0, 'sky').setOrigin(0, 0);
        bg.setDisplaySize(this.sys.game.config.width, this.sys.game.config.height);
    }

    /**
     * 足場(タイルマップ)を作成して衝突判定を設定する
     */
    createPlatforms() {
        const mapData = [];

        // 1~36行目までは空白
        for (let y = 0; y < 36; y++) {
            mapData[y] = new Array(50).fill(-1);
        }

        // 足場ブロックを配置
        mapData[2].fill(368, 0, 50);
        mapData[15].fill(368, 10, 30);
        mapData[22].fill(368, 20, 30);
        mapData[22].fill(368, 40, 70);
        mapData[30].fill(368, 20, 50);

        // 最下段(36行目)は全面にブロックを敷く
        mapData[36] = new Array(50).fill(368);

        // タイルマップを生成
        const map = this.make.tilemap({
            data: mapData,
            tileWidth: 16,
            tileHeight: 16
        });

        const tiles = map.addTilesetImage('ground');
        const layer = map.createLayer(0, tiles, 0, 0);

        // -1 以外を衝突対象に設定
        layer.setCollisionByExclusion([-1]);
    }

    /**
     * 毎フレーム呼ばれる更新処理
     * - プレイヤー操作や物理演算などを記述する
     * 
     * @param {number} [time] 経過時間(ミリ秒)
     * @param {number} [delta] 前フレームからの経過時間(ミリ秒)
     * @override
     */
    update(time, delta) {
        // ゲームループ内の処理を書く
    }
}

まずpreloadの中で足場画像を読み込んでいます。
ここではチップセット画像を分割して指定する方式をとっています。
こんな感じで一定のピクセル数でマス状に配置された集合画像をゲームではよく使います。
アニメーションとかにも使いやすかったりリソースの問題だったりで。

それで、何ピクセルごとに分割するかを指定しています。

次にcreateメソッドですが、少し整理しています。
足場を作るロジックはcreatePlatformsメソッドに切り出して、この中に書いています。
mapDataの配列に指定したところにブロックが配置される仕組みです。

マップデータの用意

Phaser では 2次元配列を渡すだけでタイルマップを生成できる 仕組みがあります。
そのため、配列に「タイルの番号」を並べることで、好きな形の足場を作れるのです。

抜粋
const mapData = [];
for (let y = 0; y < 36; y++) {
    mapData[y] = new Array(50).fill(-1);
}

ここでは 50 列 × 37 行のマップを作成しています。
-1 は「タイルなし」を意味します。
つまり最初は「空っぽのマップ」を用意しているわけです。

足場の配置

抜粋
mapData[2].fill(368, 0, 50);
mapData[15].fill(368, 10, 30);
mapData[22].fill(368, 20, 30);
mapData[22].fill(368, 40, 70);
mapData[30].fill(368, 20, 50);
mapData[36] = new Array(50).fill(368);

ここで配列の一部に 368 を埋めています。
368 は今回使用したタイルセット(画像チップの中)では「足場ブロック」に対応する番号です。

例えば mapData[2].fill(368, 0, 50); は「2 行目に横一列の足場を敷く」という意味になります。
fill の範囲を変えることで、足場の長さや位置を自由に調整できます。

一番下(36 行目)は全部 368 にして、床のように全面を埋めています。

mapData[上から何行目か].fill(タイル番号, 始点, 終点);

タイルマップを生成

抜粋
const map = this.make.tilemap({
    data: mapData,
    tileWidth: 16,
    tileHeight: 16
});

ここで Phaser に「この配列をマップとして使ってください」と渡しています。
tileWidth と tileHeight はタイルのサイズで、今回は 16×16 ピクセルです。

タイルセットを登録

抜粋
const tiles = map.addTilesetImage('ground');
const layer = map.createLayer(0, tiles, 0, 0);

タイルマップを使うには「どの画像チップを使うか」を登録する必要があります。
ここでは ground というスプライトシートを登録し、マップに紐づけています。
その結果、先ほどの配列の数値(368など)が「画像の何番目のチップか」に変換されて描画されます。

map.createLayer() の引数は以下の意味を持ちます。
map.createLayer(layerIndexOrName, tileset, x, y);
「最初のレイヤーを、ground タイルセットを使って、画面の左上 (0,0) に描画する」という意味になります。

衝突判定を設定

layer.setCollisionByExclusion([-1]);

ここでは -1 以外はすべて衝突対象としています。
つまり 368 のタイルはプレイヤーやオブジェクトとぶつかり、足場として機能するようになります。
これがないと物理演算が効かず、プレイヤーが乗ろうとしてもすり抜けて落ちていってしまいます。

さて、ここまででこんな画面になりました。

お疲れ様でした。

株式会社ONE WEDGE

【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp

Discussion