mcpField
T
tilemaps

tilemaps

github.com/phaserjs开发40.6k989 次安装10 周前更新
代码仓库

关于 tilemaps

Phaser 瓦片地图技能参考,介绍地图加载、渲染、查询和修改,适用于使用 Tiled 数据、CSV 或数组的游戏开发者,并涵盖多种地图方向及 CPU、GPU 图层。

主要功能

  1. 01Tiled JSON、CSV 和数组地图数据
  2. 02瓦片集与可渲染图层
  3. 03瓦片碰撞与物理设置
  4. 04瓦片查询与运行时编辑
  5. 05GPU 图层渲染

适用场景

  1. 01基于 Tiled 地图构建关卡
  2. 02依据瓦片属性设置碰撞
  3. 03在游戏运行时编辑地图瓦片
  4. 04根据世界坐标查询瓦片

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/phaserjs/phaser --skill tilemaps

安装后在 Claude Code 中输入/tilemaps确认已加载。

详细介绍

瓦片地图

Phaser 瓦片地图可从 Tiled JSON、CSV 或原始 2D 数组渲染基于瓦片的关卡。Tilemap 保存解析后的地图数据,并提供添加图块集、创建图层、设置碰撞和查询瓦片的方法。图层(TilemapLayer 或 TilemapGPULayer)是实际渲染瓦片的游戏对象。Phaser 支持正交、等距、六边形和交错地图。

关键源码路径: src/tilemaps/Tilemap.js、src/tilemaps/TilemapLayer.js、src/tilemaps/TilemapGPULayer.js、src/tilemaps/TilemapLayerBase.js、src/tilemaps/Tile.js、src/tilemaps/Tileset.js、src/tilemaps/TilemapFactory.js、src/tilemaps/components/、src/tilemaps/parsers/tiled/ 相关技能: ../loading-assets/SKILL.md、../sprites-and-images/SKILL.md

快速开始

class GameScene extends Phaser.Scene {
    preload() {
        // Load the Tiled JSON and the tileset image
        this.load.tilemapTiledJSON('map', 'assets/level1.json');
        this.load.image('tiles', 'assets/tilesheet.png');
    }

    create() {
        // Create the tilemap from cached JSON
        const map = this.add.tilemap('map');

        // Link the tileset image to the tileset name used in Tiled
        const tileset = map.addTilesetImage('tilesheet', 'tiles');

        // Create a layer - layerID must match the layer name in Tiled
        const ground = map.createLayer('Ground', tileset);

        // Enable collision on specific tile indexes
        ground.setCollision([1, 2, 3]);
    }
}

流程始终是:加载 JSON + 图像,创建瓦片地图,添加图块集图像,创建一个或多个图层,设置碰撞。

核心概念

Tilemap 与图层

Tilemap 是数据容器,不是显示对象。它存储解析后的地图数据(图层、图块集、对象),并提供操作这些数据的方法。TilemapLayer 或 TilemapGPULayer 是添加到显示列表、实际执行渲染的游戏对象。

const map = this.add.tilemap('map');    // Data container (not rendered)
const layer = map.createLayer('Ground', tileset);  // Game Object (rendered)

Tilemap(key) 会在缓存中查找瓦片地图数据。Tilemap 实例会被缓存。

Tileset(name, firstgid, tileWidth, tileHeight, tileMargin, tileSpacing) 将图块集名称与其在瓦片地图中的第一个 gid 关联起来。它存储 firstgid 和瓦片尺寸、边距、间距。

// tilesetName: the name in Tiled's tileset panel
// key: the Phaser texture key (defaults to tilesetName if omitted)
const tileset = map.addTilesetImage('tilesetName', 'textureKey');

// Override tile dimensions, margin, and spacing if needed
const tileset = map.addTilesetImage('name', 'key', 16, 16, 1, 2);

addTilesetImage(tilesetName, key, tileWidth, tileHeight, tileMargin, tileSpacing, gid) - 如果图块集名称已存在于解析后的地图数据中,则使用它。如果没有(非 Tiled 地图),则创建一个新的图块集。

重要: Phaser 的 Tiled 解析器不支持 "Collection of Images" 图块集。所有瓦片都必须位于单个图块集图像中。

瓦片

图层中的每个单元格都是一个 Tile 对象(src/tilemaps/Tile.js)。关键属性:

  • index - 图块集中的瓦片索引(空为 -1)
  • x、y - 瓦片坐标(以瓦片为单位,不是像素)
  • pixelX、pixelY - 相对于图层原点的像素位置
  • width、height - 瓦片大小(像素)
  • properties - 来自 Tiled 的自定义属性(对象)
  • collideLeft、collideRight、collideUp、collideDown - 各边缘碰撞标志
  • faceLeft、faceRight、faceTop、faceBottom - 用于碰撞优化的 interesting face 标志
  • collisionCallback - 每个瓦片的碰撞回调函数
  • tint - 色调颜色值(默认 0xffffff)
  • tintMode - 色调混合模式(默认 TintModes.MULTIPLY)
  • rotation - 旋转角度
  • physics - 用于物理引擎特定数据的对象(例如刚体)
  • alpha、visible、flipX、flipY - 继承自 mixin
TilemapGPULayer(v4.0.0)

TilemapGPULayer 是 TilemapLayer 的高性能、仅 WebGL 替代方案。它使用着色器将整个图层渲染为单个四边形,几乎完全由 GPU 处理。

// Pass gpu: true as the 5th argument to createLayer
const layer = map.createLayer('Ground', tileset, 0, 0, true);

能力:

  • 每层仅支持单个图块集(不支持多图块集)
  • 最大瓦片地图尺寸:4096x4096 瓦片
  • 最大唯一瓦片 ID 数:2^23(8,388,608)
  • 支持瓦片翻转和瓦片动画
  • 仅支持正交地图(不支持等距/六边形/交错)
  • 使用 LINEAR 过滤平滑瓦片边界(无缝)
  • 使用 NEAREST 过滤保持像素锐利

限制:

  • 图层编辑不会自动显示。修改瓦片后需要调用 generateLayerDataTexture()。
  • 仅支持 WebGL 渲染器(无 Canvas 回退)
  • 无法在单个图层上使用多个图块集
// If you edit tiles on a GPU layer, regenerate the data texture:
gpuLayer.putTileAt(5, 10, 10);
gpuLayer.generateLayerDataTexture();
TilemapLayerBase

TilemapLayer 和 TilemapGPULayer 都继承自 TilemapLayerBase(src/tilemaps/TilemapLayerBase.js),后者继承自 GameObject。基类提供所有瓦片查询、操作与碰撞方法。它包含以下组件混入:Alpha、BlendMode、ComputedSize、Depth、ElapseTimer、Flip、GetBounds、Lighting、Mask、Origin、RenderNodes、Transform、Visible、ScrollFactor 和 Arcade Physics Collision。

常见模式

从 Tiled JSON 创建
preload() {
    this.load.tilemapTiledJSON('map', 'assets/map.json');
    this.load.image('tiles', 'assets/tileset.png');
}

create() {
    const map = this.add.tilemap('map');
    const tileset = map.addTilesetImage('TilesetNameInTiled', 'tiles');
    const layer = map.createLayer('LayerNameInTiled', tileset);
}

传递给 createLayer 的 layerID 必须与 Tiled 中的图层名称完全一致。组图层子项会按 'ParentGroup/Layer' 命名约定展平。

多个图层
const map = this.add.tilemap('map');
const tileset = map.addTilesetImage('terrain', 'terrain-img');

const background = map.createLayer('Background', tileset);
const ground = map.createLayer('Ground', tileset);
const foreground = map.createLayer('Foreground', tileset);

// Layers are rendered in creation order. Use depth for finer control:
foreground.setDepth(10);

一个图层可以使用多个瓦片集(仅限 CPU 图层):

const tiles1 = map.addTilesetImage('terrain', 'terrain-img');
const tiles2 = map.addTilesetImage('objects', 'objects-img');
const layer = map.createLayer('Ground', [tiles1, tiles2]);
创建空白图层
const map = this.add.tilemap('map');
const tileset = map.addTilesetImage('terrain', 'terrain-img');

// createBlankLayer(name, tileset, x, y, width, height, tileWidth, tileHeight)
const layer = map.createBlankLayer('dynamic', tileset, 0, 0, 50, 50, 32, 32);

// Fill it with tiles
layer.fill(1);  // Fill entire layer with tile index 1
layer.putTileAt(5, 10, 10);  // Place tile index 5 at tile coord (10, 10)
碰撞设置

有几种方式可以为 Arcade Physics 启用瓦片碰撞:

// By specific tile indexes
layer.setCollision([1, 2, 3]);

// By range (inclusive)
layer.setCollisionBetween(1, 50);

// By tile property (set in Tiled's tileset editor)
layer.setCollisionByProperty({ collides: true });
// Supports arrays: { type: ['stone', 'lava'] }

// By exclusion - collide on ALL tiles except these
layer.setCollisionByExclusion([-1, 0]);  // -1 is empty, 0 is often background

// From Tiled collision editor shapes
layer.setCollisionFromCollisionGroup();

TilemapLayerBase 上的所有碰撞方法都镜像了 Tilemap 上的方法,但不需要 layer 参数。在 Tilemap 上,你可以传入图层引用或使用“当前图层”:

map.setLayer('Ground');
map.setCollision([1, 2, 3]);  // Applies to current layer
// Or specify a layer explicitly:
map.setCollision([1, 2, 3], true, true, 'Ground');
物理集成(Arcade)
// Enable collisions between a sprite and a tilemap layer
this.physics.add.collider(player, groundLayer);

// With a callback
this.physics.add.collider(player, groundLayer, (sprite, tile) => {
    if (tile.index === 5) {
        // Hit a special tile
    }
});

// Overlap detection instead of collision
this.physics.add.overlap(player, groundLayer, (sprite, tile) => {
    // Player is overlapping this tile
});

图层必须在其瓦片上设置碰撞(通过 setCollision* 方法),物理系统才能检测到它们。图层本身具有 collisionCategory 和 collisionMask 属性,用于碰撞过滤。

瓦片属性

瓦片可以在 Tiled 的瓦片集编辑器中设置自定义属性:

// Access tile properties
const tile = layer.getTileAt(10, 5);
console.log(tile.properties.damage);    // Custom property from Tiled
console.log(tile.properties.type);      // Custom property from Tiled

// Set collision based on custom properties
layer.setCollisionByProperty({ collides: true });
layer.setCollisionByProperty({ type: ['wall', 'rock'] });
瓦片回调
// Callback by tile index - fires when physics body overlaps these tiles
map.setTileIndexCallback([5, 6, 7], (sprite, tile) => {
    // Called for tiles with index 5, 6, or 7
    console.log('Hit tile', tile.index, 'at', tile.x, tile.y);
}, this);

// Callback by tile location - fires for tiles in a rectangular area
map.setTileLocationCallback(10, 10, 5, 5, (sprite, tile) => {
    // Called for any tile in the 5x5 region starting at (10, 10)
}, this);

// Per-tile callback
const tile = layer.getTileAt(10, 5);
tile.collisionCallback = (sprite, tile) => {
    // Custom logic for this specific tile
};

瓦片回调需要刚体与图层之间存在活动的物理碰撞器/重叠。

查询瓦片
const tile = layer.getTileAt(10, 5);               // By tile coords (or null)
const tile = layer.getTileAt(10, 5, true);          // nonNull: Tile with index -1 instead of null
const tile = layer.getTileAtWorldXY(worldX, worldY); // By world coords
const exists = layer.hasTileAt(10, 5);              // Boolean check

// Region queries
const tiles = layer.getTilesWithin(0, 0, 10, 10);            // Tile coord region
const tiles = layer.getTilesWithinWorldXY(x, y, w, h);       // World coord region
const tiles = layer.getTilesWithinShape(circle);              // Shape overlap

// Functional queries
const water = layer.filterTiles(t => t.properties.type === 'water');
const spawn = layer.findTile(t => t.properties.isSpawn);
layer.forEachTile(t => { /* iterate all tiles */ });
在运行时修改瓦片
layer.putTileAt(5, 10, 10);                      // Place tile index 5 at (10, 10)
layer.putTileAtWorldXY(5, worldX, worldY);        // Place by world coords
layer.putTilesAt([[1, 2], [3, 4]], 10, 10);       // Place a 2x2 grid
layer.removeTileAt(10, 10);                       // Remove tile
layer.fill(1, 0, 0, 10, 10);                     // Fill 10x10 region with index 1
layer.replaceByIndex(5, 10);                      // Replace all index-5 with index-10
layer.copy(0, 0, 5, 5, 20, 20);                  // Copy 5x5 from (0,0) to (20,20)
layer.randomize(0, 0, 10, 10, [1, 2, 3, 4]);     // Random tiles in region
layer.weightedRandomize([{ index: 1, weight: 4 }, { index: 2, weight: 1 }], 0, 0, 10, 10);
layer.shuffle(0, 0, 10, 10);                     // Shuffle tiles in region
坐标转换
const tileXY = layer.worldToTileXY(worldX, worldY);   // World -> tile coords
const worldXY = layer.tileToWorldXY(tileX, tileY);    // Tile -> world coords

// Reuse a vector to avoid allocation
const vec = new Phaser.Math.Vector2();
layer.worldToTileXY(worldX, worldY, true, vec);  // snapToFloor = true
对象图层(Tiled)

Tiled 对象图层定义点、矩形和精灵放置。使用 Tilemap 上的 createFromObjects:

// Create sprites from all objects on the 'Enemies' object layer
const enemies = map.createFromObjects('Enemies', {
    gid: 26,          // Match by tile GID
    classType: Enemy   // Custom class extending Sprite
});

// Match by name
const coins = map.createFromObjects('Items', {
    name: 'coin',
    key: 'coin-texture',
    frame: 0
});

// Match by type
const spawns = map.createFromObjects('Spawns', {
    type: 'player-spawn'
});

// Access raw object layer data
const objectLayer = map.getObjectLayer('Enemies');
objectLayer.objects.forEach(obj => {
    console.log(obj.name, obj.x, obj.y, obj.properties);
});

createFromObjects(layerName, config, useTileset) 配置选项:id、gid、name、type、classType(默认 Sprite)、scene、container、key、frame、ignoreTileset。

动画瓦片

瓦片动画在 Tiled 的瓦片集编辑器中定义并自动解析。TilemapLayer 和 TilemapGPULayer 都支持动画瓦片。TilemapLayerBase 使用 ElapseTimer 通过 preUpdate 跟踪动画时间。

等距、六边形和交错地图
// Isometric map
const map = this.add.tilemap('iso-map');
const tileset = map.addTilesetImage('iso-tiles', 'iso-img');
const layer = map.createLayer('Ground', tileset);

// Get tile at world coords in isometric space
const tile = layer.getIsoTileAtWorldXY(worldX, worldY);

// TilemapGPULayer does NOT support iso/hex/staggered - use TilemapLayer

地图的 orientation 属性从 Tiled 数据设置。坐标转换函数会根据方向自动选择。

API 快速参考

Tilemap(数据容器 - 不渲染)
方法描述
addTilesetImage(name, key, tw, th, margin, spacing, gid, offset)将瓦片集名称链接到纹理
createLayer(layerID, tileset, x, y, gpu)创建图层(gpu=true 用于 GPU 图层)
createBlankLayer(name, tileset, x, y, w, h, tw, th)为程序化地图创建空图层
createFromObjects(layerName, config, useTileset)将 Tiled 对象转换为 Sprite
getObjectLayer(name)获取原始对象图层数据
setLayer(layer)为简写方法设置当前活动图层

大多数瓦片查询/碰撞/操作方法同时存在于 Tilemap(带额外 layer 参数)和 TilemapLayerBase(不带)上。优先直接在图层上调用。

TilemapLayerBase(渲染图层 - CPU 和 GPU)

碰撞: setCollision(indexes)、setCollisionBetween(start, stop)、setCollisionByProperty(props)、setCollisionByExclusion(indexes)、setCollisionFromCollisionGroup()、setTileIndexCallback(indexes, cb, ctx)、setTileLocationCallback(x, y, w, h, cb, ctx)

瓦片查询: getTileAt(x, y, nonNull)、getTileAtWorldXY(wx, wy, nonNull, cam)、getTilesWithin(x, y, w, h, opts)、getTilesWithinWorldXY(wx, wy, w, h, opts, cam)、getTilesWithinShape(shape, opts, cam)、hasTileAt(x, y)、hasTileAtWorldXY(wx, wy, cam)、filterTiles(cb)、findTile(cb)、forEachTile(cb)

瓦片操作: putTileAt(tile, x, y)、putTileAtWorldXY(tile, wx, wy)、putTilesAt(arr, x, y)、removeTileAt(x, y)、fill(index, x, y, w, h)、copy(sx, sy, w, h, dx, dy)、randomize(x, y, w, h, indexes)、weightedRandomize(weights, x, y, w, h)、shuffle(x, y, w, h)、swapByIndex(a, b)、replaceByIndex(find, replace)、createFromTiles(indexes, replacements, config)

坐标: worldToTileXY(wx, wy, snap, vec, cam)、tileToWorldXY(tx, ty, vec, cam)

TilemapGPULayer(补充)
方法描述
generateLayerDataTexture()在瓦片编辑后重新生成 GPU 纹理
瓦片属性

index(number,-1=空)、x/y(瓦片坐标)、pixelX/pixelY(相对于图层的像素位置)、width/height、properties(来自 Tiled 的对象)、collideLeft/Right/Up/Down(boolean)、collisionCallback(function)、tint(number)、rotation(number)、alpha、flipX/flipY、physics(用于引擎数据的对象)

注意事项

  1. Tileset 名称必须与 Tiled 完全一致。 addTilesetImage 的第一个参数是 Tiled 中定义的 tileset 名称,而不是 Phaser 纹理键。如果它们不匹配,你会得到 null 并在控制台看到警告。

  2. 图层名称必须与 Tiled 完全一致。 createLayer 接受 Tiled 中的图层名称(或图层索引)。组图层的子图层会加上 'GroupName/LayerName' 前缀。

  3. 每个图层只能创建一次。 用相同的图层 ID 调用 createLayer 两次会返回 null 并给出警告。图层数据只能与一个图层 GameObject 关联。

  4. 必须先调用 setCollision,物理碰撞器才能生效。 如果不将瓦片标记为可碰撞,this.physics.add.collider() 会直接穿过所有瓦片。

  5. TilemapGPULayer 仅支持正交投影。 它不支持等距、六边形或交错地图。它每个图层也只支持单个 tileset。

  6. TilemapGPULayer 需要手动重新生成纹理。 调用 putTileAt 或其他编辑方法后,需调用 generateLayerDataTexture(),否则更改不会显示。

  7. 不支持 "Collection of Images" tileset。 Tiled 解析器要求 tileset 中的所有瓦片都位于单张图片中。导出的 JSON 中必须包含内嵌的 tileset。

  8. 瓦片索引 -1 表示空。 许多方法默认对空瓦片返回 null。传入 nonNull: true 可获得一个 index === -1 的 Tile 对象。

  9. tilemap 工厂中的 insertNull。 创建 tilemap 时,insertNull: true 会为空瓦片存储 null,而不是 index 为 -1 的 Tile 对象。这可为大型稀疏地图节省内存,但会阻止在空单元格中动态放置瓦片。

  10. 瓦片回调仅在物理激活时触发。 setTileIndexCallback 和 setTileLocationCallback 需要物理碰撞器或刚体与图层之间的重叠才能触发。

  11. 图层位置与 Tiled 偏移。 如果在 createLayer 中未指定 x 和 y,它们会默认为 Tiled 中定义的图层偏移,而不是 (0, 0)。

源文件

文件用途
src/tilemaps/Tilemap.js主数据容器,包含所有地图级方法
src/tilemaps/TilemapFactory.js在 GameObjectFactory 上注册 this.add.tilemap()
src/tilemaps/TilemapLayerBase.jsCPU 与 GPU 图层共用的基类(继承自 GameObject)
src/tilemaps/TilemapLayer.jsCPU 渲染图层(支持多图块集、所有图块集)
src/tilemaps/TilemapGPULayer.jsGPU 加速图层(v4、WebGL、正交投影、单一图块集)
src/tilemaps/Tile.js单个图块数据(索引、位置、碰撞、属性)
src/tilemaps/Tileset.js图块集数据(名称、firstgid、尺寸、图像)
src/tilemaps/components/纯函数:SetCollision、GetTileAt、PutTileAt、SetTileIndexCallback
src/tilemaps/parsers/tiled/Tiled JSON 解析器:ParseJSONTiled、ParseTileLayers、ParseObjectLayers、ParseTilesets、BuildTilesetIndex
src/tilemaps/ParseToTilemap.js协调解析与 Tilemap 创建
src/tilemaps/mapdata/Layer.js图层数据结构

评论

支持 Markdown