旧プログラマーによる日曜プログラミング日記

趣味は作曲、自分で一から作りたがりのユーザIT子会社の旧プログラマーによるリハビリのための日曜プログラミング日記です。

Phaser3+typescriptでSRPGを開発してみる #004 マップ作成

こんにちは!

次に着手したいのが、まずは動くものを作りたい、ということでタイルチップの読み込みです。

タイルチップとは、昔ながらの2D(3Dもそうかもですが)ゲームを作るときのバックの絵のことです。 スーパーマリオのブロックとか、ドラクエの地図マップとか、そんなイメージで、 SRPG作るときにもまず必要になりそうな気がします。

マップ作成(1)

マップのタイルチップ

まずはタイルチップを取得して画像ファイルを読み込みます。

今回は、ドット絵世界、から草原のタイルチップをお借りしてまいりました。

f:id:mtmusic34:20210612190507p:plain
草原チップ
yms.main.jp

画像ファイル(png等)はpreload()関数の中で読み込みます。

this.load.image("PLAIN_MAP", "/images/map/tileset_demo.png");

preload()関数は、Phaser3のフレームワークが順次呼び出すライフサイクル関数のうちの一つです。

ライフサイクル

Phaser.Sceneクラスを継承したクラスは、下記メソッドを呼び出せます。

関数名 概要
init preloadに先駆けて呼ばれるメソッド。どんな場合に使うのかはもう少し確認が必要
preload アセット(画像・音楽・データ等)を呼び出すときに使用
create シーンスタート時にゲームオブジェクトを作るときに使用
update シーンに対して更新を掛けるときに使用。FPSの指定タイミング間隔でよびだされる

initのタイミングだけはよくわからず、今後サンプルとか見てみようと思いますが、 その他のpreload、create、updateの3関数を使いこなせれば、ゲームは作れそうです。

では、GameScene.tsを修正してみます。

GameScene.ts

export default class GameScene extends Phaser.Scene {

    // 1セルあたりの解像度(px)
    public static CELL_PX = {
        WIDTH : 32,
        HEIGHT : 32
    };

    // マップの開始位置
    private static MAP_POSITION = {
        X :  20,
        Y :  40
    };

    // タイルセットの画像ファイルのキー
    private static TILESET_KEY_PLAIN = "PLAIN_MAP";

    preload() {
        // 画像ファイルをロード(キー・ファイルパス)
        this.load.image(GameScene.TILESET_KEY_PLAIN, "/images/map/tileset_demo.png");
    }

    create() {

        // マップデータの指定(タイルチップの番号)
        let mapData: number[][] = 
            [
                [3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
                [3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
                [3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
                [3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
                [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]        
            ];

        // タイルマップの作成
        var tilemap = this.make.tilemap({
                data: mapData,                      // マップデータ 
                tileWidth: GameScene.CELL_PX.WIDTH, // タイルの単位幅(px)
                tileHeight: GameScene.CELL_PX.HEIGHT    // タイルの単位高さ(px)
        });
        // タイルセットをリンク付け
        let planeTiles = tilemap.addTilesetImage(GameScene.TILESET_KEY_PLAIN);
        // 一番下のレイヤーを作成 
        // 第3引数・第4引数は、左上の座標を(0,0)とした時の配置位置(x,y) 
        let mapGroundLayer = tilemap.createLayer(
            0, planeTiles, 
            GameScene.MAP_POSITION.X, 
            GameScene.MAP_POSITION.Y);

    }
}

TilemapConfig

this.make.tilemap()の引数がTilemapConfigです。

dataには、配置したいマップのデータを配列でセットします。

mapData変数にて定義していますが、今回は全て3を指定してます。

こうすると、画像ファイルの左上から3番目(と言いながら、0番目から開始するので、実質左から4番目)が呼ばれます。

f:id:mtmusic34:20210613215518p:plain
インデックス番号をつけてみた

この画像ファイルは32×32を1セルとして構成されています。その指定が、tileWidthtileHeightです。 今回は、クラス内部の定数として定義してみました(この辺りが、古いJavaプログラマーの発想なのかも)。

ちなみに、画像ファイルの場所が誤っていると、

Invalid Tileset Image: PLAIN_MAP

というエラーが出ます。

より複雑なマップに

今回は全て3にしましたが、試しに湖を中に作ってみようとしますと、

        let mapData: number[][] = 
            [
                [3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
                [3, 3, 3, 3, 26, 11, 27, 3, 3, 3],
                [3, 3, 3, 3, 11, 11, 11, 3, 3, 3],
                [3, 3, 3, 3, 42, 11, 43, 3, 3, 3],
                [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]        
            ];

f:id:mtmusic34:20210613214239p:plain
試しに作った湖

なんか不格好ですよね。。

まあ、湖の中の黒部分はさっきの画像を0.5個分、ずらしたタイルマップを作ればいけそうです。

ただ、縁の部分はそうはいかないです。。画像ファイルは透過対応してくれるのですが、一番下の背景が真っ黒ですからね。。

もしかしたらレイヤー化したら変わるかも?とも思い、create()の記載を、Layer ID:0用とID:1用を続けてプログラミングしてみたのですが、この結果は無情にもID:0用しか反映されず、草原のみ表示。わざわざID:1用の配置位置をtilemapLayer1.createLayer(1, planeTiles1, 20,200);と重ならないようにy軸をずらしたのに。。

エラーを見ると Invalid Tilemap Layer ID: 1。単に認識されていないようです。。

では、このLayer IDはどこで指定しているのか?

Phaser3のソースコード(OSSなので。。)の中身を見てみると、どうやら、

  • デフォルトでtilemapを読み込むと、number型の0が入る
        var index = this.getLayerIndex(layerID);

        if (index === null)
        {
            console.warn('Invalid Tilemap Layer ID: ' + layerID);
  • tilemap読み込むときにLayerIDを指定する? といった特徴がありそう。。

そんな中、マップ作成を支援してくれそうなソフトの存在も発見!

すみません、、後半(#005)へ続きます。 mtmusic34.hatenablog.com

過去の日記

タイトル 記載内容
#001 プロローグ Phaser3とは
#002 環境設定 インストール・ビルド環境設定
#003 はじめてのPhaser3 初回稼働・キャンバス表示