Phaserでマップを利用した仕組み
tilemapを利用したマップの仕組み
マップエディタ(Tiled)で作成したtilemapを利用して、マップを作成します。初歩的なtilemapを利用したパターンです
マップの使用
- タイルは64x64
- 横は12マス、縦は12マス
- レイヤーは1個だけ、レイヤー名はstage
- レイヤーstageにはプレイヤーは衝突する
ここで目指すもの
- とりあえずtilemapの表示
- プレイヤーは上下左右に移動、1個の移動で64移動する
- プレイヤーは移動時にアニメーションする
マップ内にライトを導入してプレイヤー位置をライトで照らす仕組みは次の課題にする
利用するスプライト画像
以下の画像を利用したtilemapとプレイヤーを作成します。1個のタイルのサイズは横64、縦64です
preloadメソッド
this.load.spritesheetでスプライト画像を読み込みます。frameWidthとframeHeightでタイルのサイズを指定します。マップデータはJSONでエクスポートします。this.load.tilemapTiledJSONで読み込みます
mainScene.preload = function() {
// タイル画像
this.load.spritesheet('tile', 'assets/images/sokoban_tilesheet.png', {
frameWidth: 64,
frameHeight: 64,
});
// マップのJSONファイルの読み込み
this.load.tilemapTiledJSON('map01', 'assets/data/map01.json');
};
createメソッド
マップとプレイヤーのみを作成します
mainScene.create = function() {
// マップ作製
this.createMap();
// プレイヤー作成
this.createPlayer();
};
tilemapによるマップデータを表示
マップを表示します
mainScene.createMap = function() {
// 中略
};
JSONデータmap01を読み込みます。
// JSON形式のマップデータの読み込み Tilemapオブジェクトの作成
this.map = this.make.tilemap({key: 'map01'});
マップデータに対応したスプライト画像を反映
第1引数はmapデータ内のタイル名、第2引数はpreloadで読み込んだ画像の名前です。戻り値groundTilesは画像情報を持つTilesetオブジェクトです
// タイル画像をマップデータに反映する Tilesetオブジェクトの作成
const groundTiles = this.map.addTilesetImage("tile", "tile");
画像の倍率調整のための計算
これはなくてもいいけど、サイズ調整のために。
// 地面レイヤー作成 DynamicTilemapLayerオブジェクト作成
const layerWidth = 64 * 1 * 12;
const layerHeight = 64 * 1 * 12;
レイヤーの作成
マップデータ内のレイヤstageを作成します。第1引数はレイヤー名、第2引数はレイヤーに反映するTilesetオブジェクトです。第3引数はX座標、第4引数はY座標です。レイヤーのサイズは上記で計算したサイズに設定します
this.groundLayer = this.map.createDynamicLayer('stage', groundTiles, 0, 0);
this.groundLayer.setDisplaySize(layerWidth, layerHeight);
レイヤーの衝突設定
レイヤーでタイルが設定されていないマスのインデックスは「-1」なので、「-1」のタイル、つまりからのタイルの位置には設定しないようにする。逆に言えば、空のタイル以外の場所はすべて衝突する。
this.groundLayer.setCollisionByExclusion([-1]);
ゲーム全体のサイズを変更
レイヤーのサイズとゲームシーン全体のサイズを連動します。今回は、マップが大きくないのであまり意味はないです
// ゲームワールドの幅と高さの設定
this.physics.world.bounds.width = this.groundLayer.displayWidth;
this.physics.world.bounds.height = this.groundLayer.displayHeight;
// カメラの表示サイズの設定をする。マップのサイズがカメラの表示サイズ
this.cameras.main.setBounds(0, 0, this.physics.world.bounds.width, this.physics.world.bounds.height);
プレイヤー作成
createPlayerでプレイヤーキャラを作成します。プレイヤーの画像は、マップに利用したスプライト画像します。プレイヤーの上下左右アニメーションもスプライト画像を利用します
mainScene.createPlayer = function() {
// プレイヤー作成
this.player = this.physics.add.sprite(32, 64 + 32, 'tile');
// 衝突サイズの調整
// プレイヤーのサイズ変更
this.player.setDisplaySize(64,64);
// プレイヤーの最初の向きは右
this.player.setFrame(52);
// プレイヤーの衝突時のバウンス設定
this.player.setBounce(0);
// プレイヤーがゲームワールドの外に出ないように衝突させる
this.player.setCollideWorldBounds(true);
// プレイヤーが地面レイヤーと衝突する設定
this.physics.add.collider(this.player, this.groundLayer);
// 移動量
this.player.dx = 64;
this.player.dy = 64;
// 下向きのアニメーション
this.anims.create({
key: 'down',
frames: this.anims.generateFrameNumbers('tile', { start: 52, end: 54 }),
frameRate: 10,
repeat: -1
});
// 上向きのアニメーション
this.anims.create({
key: 'up',
frames: this.anims.generateFrameNumbers('tile', { start: 55, end: 57 }),
frameRate: 10,
repeat: -1
});
// 左向きのアニメーション
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('tile', { start: 81, end: 83 }),
frameRate: 10,
repeat: -1
});
// 右向きのアニメーション
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('tile', { start: 78, end: 80 }),
frameRate: 10,
repeat: -1
});
this.cursors = this.input.keyboard.createCursorKeys();
};
updateメソッド
プレイヤーの移動処理を行います。タイルの画像に合わせて移動します。横64、縦64の移動です。移動に合わせてアニメーションを行います
mainScene.update = function() {
// 中略
};
左方向の移動
this.input.keyboard.checkDownはようするにonkeydownを細かく調整するためのメソッドです。第1引数はキーがプレスダウンされているかを確認するキー、第2引数は待機時間のミリ秒です。ここでは左カーソルキーがプレスダウンされているかどうかを判定します
左カーソルキーであれば、プレイヤーのX座標から64差し引いた一のタイルを取り出します。タイルのindexがー1であれば、空のタイルなので、X座標を移動します。同時に左アニメーションを実行します
if (this.input.keyboard.checkDown(this.cursors.left, 100)) {
const tile = this.groundLayer.getTileAtWorldXY(this.player.x - this.player.dx, this.player.y, true);
if (tile.index === -1) {
this.player.x -= this.player.dx;
this.player.anims.play('left', true);
}
全体の移動判定
上下左右の移動は以下の通りです
mainScene.update = function() {
if (this.input.keyboard.checkDown(this.cursors.left, 100)) {
const tile = this.groundLayer.getTileAtWorldXY(this.player.x - this.player.dx, this.player.y, true);
if (tile.index === -1) {
this.player.x -= this.player.dx;
this.player.anims.play('left', true);
}
} else if (this.input.keyboard.checkDown(this.cursors.right, 100)) {
const tile = this.groundLayer.getTileAtWorldXY(this.player.x + this.player.dx, this.player.y, true);
if (tile.index === -1) {
this.player.x += this.player.dx;
this.player.anims.play('right', true);
}
} else if (this.input.keyboard.checkDown(this.cursors.up, 100)) {
const tile = this.groundLayer.getTileAtWorldXY(this.player.x, this.player.y - this.player.dy, true);
if (tile.index === -1 ) {
this.player.y -= this.player.dy;
this.player.anims.play('up', true);
}
} else if (this.input.keyboard.checkDown(this.cursors.down, 100)){
const tile = this.groundLayer.getTileAtWorldXY(this.player.x, this.player.y + this.player.dy, true);
if (tile.index === -1) {
this.player.y += this.player.dy;
this.player.anims.play('down', true);
}
} else {
this.player.anims.stop();
}
};
実行結果
Tilemapを利用したマップを表示します。プレイヤーはマップ内を移動します。左上の角のみプレイヤーが飛び出すとエラーになります。厳密には、移動する際にタイルがない場合、エラーになるので、移動の判定をするようにしないといけないです
プレイヤーの移動に合わせて、ライトで周辺を照らすこと、ライト以外の場所を暗くすることをやりたいのですが、それは次に目指す。
最終的に実現したコード
Discussion