mcpField
P
phaser-tilemap

phaser-tilemap

github.com/yakoub-ai開発29194 回インストール9 週間前に更新
リポジトリ

phaser-tilemap について

phaser-tilemap は、Tiled の JSON マップを Phaser 4 で扱うためのスキルです。マップ作成からレイヤーやオブジェクト、衝突設定、実行中のタイル操作までを説明します。

主な機能

  1. 01Tiled JSON マップのワークフロー
  2. 02タイルレイヤーとオブジェクトの設定
  3. 03プロパティやインデックスによる衝突設定
  4. 04カメラと物理ワールド境界の設定
  5. 05実行中のタイル編集と視差表現

活用シーン

  1. 01Phaser 4 用 Tiled マップの作成
  2. 02地面や危険レイヤーの設定
  3. 03スポーン地点や敵オブジェクトの読み込み
  4. 04タイル衝突のデバッグ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yakoub-ai/phaser4-gamedev --skill phaser-tilemap

インストール後、Claude Code で/phaser-tilemapを入力して読み込まれたか確認します。

同梱ファイルreferences/tiled-workflow.mdreferences/tilemap-api.md

詳細

Phaser 4 タイルマップ

Phaser 4 は Tiled マップファイル(JSON 形式)をファーストクラスでサポートしています。推奨されるワークフローは次のとおりです: Tiled で設計 → JSON をエクスポート → Phaser で読み込み → レイヤーを作成 → 衝突を設定。

Tiled エディターのワークフロー

Tiled は mapeditor.org から無料でダウンロードできます。

新しいマップの作成
  1. File → New → New Map
  2. Orientation を Orthogonal(ほとんどの 2D ゲーム)または Isometric(トップダウン RPG/ストラテジー)に設定
  3. Tile layer format を CSV または Base64 に設定 — どちらも Phaser で動作します
  4. Tile size をタイルセットに合わせて設定(一般的には 16x16、32x32)
  5. Map size をタイル単位で設定(例: 32px タイルで 1280x736 のマップなら横 40 × 縦 23)
タイルセットの追加
  1. Tilesets パネル(右下)で + ボタンをクリック → New Tileset
  2. Name を設定 — この名前がそのまま map.addTilesetImage() の第 1 引数として使用されます
  3. Type を Based on Tileset Image に設定
  4. PNG を選択し、タイルの幅/高さを一致するように設定
  5. Margin と spacing: タイルセットのタイル間に余白がある場合は設定します(多くの場合は 0)
衝突タイルのマーク
  1. Tilesets パネルでタイルセットを選択
  2. レンチアイコン(Edit Tileset)をクリックしてタイルセットエディターを開く
  3. 衝突させるタイルを選択(クリックで選択、Ctrl+クリックで複数選択)
  4. Properties パネル(左側)で + ボタンをクリックしてプロパティを追加
  5. Name を collides、Type を bool、Value を true に設定
  6. タイルセットエディターを閉じる

Phaser では layer.setCollisionByProperty({ collides: true }) を呼び出してこれらのタイルを有効にします。

レイヤー命名規則

一貫したレイヤー名を使用してください — Phaser は文字列でレイヤーを参照します:

レイヤー名種類目的
Backgroundタイルレイヤー空、遠景 — 衝突なし
Groundタイルレイヤー主な歩行可能な表面 — 衝突有効
Hazardsタイルレイヤートゲ、溶岩 — オーバーラップ(衝突ではない)
Foregroundタイルレイヤープレイヤーの手前に描画される木、アーチ
Objectsオブジェクトレイヤースポーンポイント、トリガー、敵
オブジェクトレイヤーの使用方法

Tiled のオブジェクトレイヤーを使うと、Phaser から問い合わせできる名前付きのポイント、矩形、ポリゴンを配置できます。

名前付きオブジェクト(プレイヤースポーンなどの一意なものに使用):

  1. Objects という名前のオブジェクトレイヤーを追加
  2. Rectangle ツールを選択し、マップ上にオブジェクトを配置
  3. Properties で Name を PlayerSpawn に設定

型付きオブジェクト(敵などの同じ種類のグループに使用):

  1. オブジェクトを配置し、Type(Tiled 1.8 では Class)を Enemy に設定
  2. カスタムプロパティを追加: + をクリックし、health(int、100)、patrol(bool、true)を追加
エクスポート設定
  1. File → Export As → JSON Map Files(.json)
  2. エクスポートオプションで Embed tilesets を有効化 — これにより外部の .tsx 依存を回避できます
  3. public/assets/tilemaps/level1.json に保存
  4. タイルセットの PNG を public/assets/images/terrain.png に配置

アセットの読み込み

preload(): void {
  // Key must match first arg of map.addTilesetImage()
  this.load.tilemapTiledJSON('level1', 'assets/tilemaps/level1.json');

  // Key must match second arg of map.addTilesetImage()
  this.load.image('terrain', 'assets/images/terrain.png');
}

マップとレイヤーの作成

private map!: Phaser.Tilemaps.Tilemap;
private groundLayer!: Phaser.Tilemaps.TilemapLayer;

create(): void {
  this.map = this.make.tilemap({ key: 'level1' });

  // First arg: tileset name as set in Tiled (must match exactly, case-sensitive)
  // Second arg: the this.load.image() key
  const tileset = this.map.addTilesetImage('terrain', 'terrain');

  // Decorative background — no collision
  const bgLayer = this.map.createLayer('Background', tileset!, 0, 0);

  // Main ground layer — collision enabled below
  this.groundLayer = this.map.createLayer('Ground', tileset!, 0, 0)!;

  // Foreground renders above the player
  const fgLayer = this.map.createLayer('Foreground', tileset!, 0, 0);
  fgLayer!.setDepth(10);  // player depth should be 1–9
}

タイルマップが表示されないよくある原因: addTilesetImage のタイルセット名が Tiled で設定した名前と完全に一致していないことです。JSON ファイルを開き、"tilesets" 内の "name" フィールドを確認してください。


衝突

プロパティによる方法(推奨)

Tiled のタイルセットエディターで設定した collides: true プロパティを使用します:

this.groundLayer.setCollisionByProperty({ collides: true });
this.physics.add.collider(this.player, this.groundLayer);
タイルインデックス範囲による方法

GID(グローバル ID)1 から 10 のタイルを衝突させます:

this.groundLayer.setCollisionBetween(1, 10);
除外による方法

空(-1)と特定のインデックスを除くすべてのタイルを衝突させます:

this.groundLayer.setCollisionByExclusion([-1, 0]);
デバッグ描画

開発中に衝突タイルを可視化します:

const debugGraphics = this.add.graphics();
this.groundLayer.renderDebug(debugGraphics, {
  tileColor: null,                                       // non-colliding tiles
  collidingTileColor: new Phaser.Display.Color(243, 134, 48, 128),  // orange
  faceColor: new Phaser.Display.Color(40, 39, 37, 255), // face outlines
});

複数のレイヤー

create(): void {
  const tileset = this.map.addTilesetImage('terrain', 'terrain')!;

  const bgLayer    = this.map.createLayer('Background', tileset, 0, 0);  // depth 0 (default)
  const groundLayer = this.map.createLayer('Ground', tileset, 0, 0)!;    // depth 0 (default)
  const fgLayer    = this.map.createLayer('Foreground', tileset, 0, 0);

  fgLayer!.setDepth(10);          // renders above player

  groundLayer.setCollisionByProperty({ collides: true });

  // Player and enemies should have depth between 1 and 9
  this.player.setDepth(5);
}

オブジェクトレイヤー

Tiled のオブジェクトレイヤーからスポーンポイント、トリガー、エンティティの配置を読み取ります:

create(): void {
  // Find a single named object — returns the first match
  const spawnPoint = this.map.findObject('Objects', obj => obj.name === 'PlayerSpawn');
  this.player = this.physics.add.sprite(spawnPoint!.x!, spawnPoint!.y!, 'player');

  // Get all objects of a given type (Tiled "Class" field in 1.8+)
  const enemyObjects = this.map.filterObjects('Objects', obj => obj.type === 'Enemy');
  enemyObjects?.forEach(obj => {
    // Access custom properties as an array: obj.properties
    const props = this.parseProperties(obj.properties);
    this.spawnEnemy(obj.x!, obj.y!, props.health ?? 100);
  });

  // Find trigger zones (rectangles placed in Tiled)
  const triggers = this.map.filterObjects('Objects', obj => obj.type === 'Trigger');
  triggers?.forEach(obj => {
    const zone = this.add.zone(obj.x! + obj.width! / 2, obj.y! + obj.height! / 2, obj.width!, obj.height!);
    this.physics.world.enable(zone);
    this.physics.add.overlap(this.player, zone, () => {
      console.log(`Entered trigger: ${obj.name}`);
    });
  });
}

// Helper: convert Tiled properties array to plain object
private parseProperties(props?: { name: string; value: unknown }[]): Record<string, unknown> {
  if (!props) return {};
  return Object.fromEntries(props.map(p => [p.name, p.value]));
}

カメラとワールド境界

プレイヤーがマップの外に出られないように、常にワールド境界をマップサイズに設定してください:

create(): void {
  // Constrain physics bodies
  this.physics.world.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);

  // Constrain camera
  this.cameras.main.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);

  // Follow the player
  this.cameras.main.startFollow(this.player, true, 0.1, 0.1);  // lerp x/y = 0.1 for smooth follow
}

動的なタイル操作

実行時にタイルを変更します(破壊可能な地形、スイッチなど):

// Read a tile at a world position
const tile = this.groundLayer.getTileAtWorldXY(ptr.worldX, ptr.worldY);
if (tile) {
  console.log(`Tile index: ${tile.index}`);
}

// Place a tile at a world position
this.groundLayer.putTileAtWorldXY(5, ptr.worldX, ptr.worldY);

// Remove a tile (sets index to -1 / empty)
this.groundLayer.removeTileAtWorldXY(ptr.worldX, ptr.worldY);

// Convert between world and tile coordinates
const tileXY = this.groundLayer.worldToTileXY(worldX, worldY)!;
const worldXY = this.groundLayer.tileToWorldXY(tileXY.x, tileXY.y)!;

パララックスレイヤー

カメラとは異なる速度でレイヤーをスクロールさせて奥行きを生み出します:

create(): void {
  const cloudLayer = this.map.createLayer('Clouds', tileset!, 0, 0);
  cloudLayer!.setScrollFactor(0.2);  // moves at 20% camera speed (furthest back)

  const hillLayer = this.map.createLayer('Hills', tileset!, 0, 0);
  hillLayer!.setScrollFactor(0.5);   // moves at 50% camera speed

  // Ground layer
  const groundLayer = this.map.createLayer('Ground', tileset!, 0, 0);
  groundLayer!.setScrollFactor(1);   // moves at 100% (default)
}

複数のタイルセット

1 つのレイヤーで複数のタイルセットのタイルを使用できます:

create(): void {
  const tiles1 = this.map.addTilesetImage('tileset-a', 'tiles-a');
  const tiles2 = this.map.addTilesetImage('tileset-b', 'tiles-b');

  // Pass an array of tilesets — Phaser resolves tile GIDs automatically
  const layer = this.map.createLayer('Ground', [tiles1!, tiles2!], 0, 0);
}

追加リソース

参照ファイル
  • references/tilemap-api.md — Tilemap、TilemapLayer、Tileset、MapData の完全な API リファレンス
  • references/tiled-workflow.md — よくある間違いを交えた、Tiled Editor の詳細なステップバイステップセットアップガイド

コメント