Phaser 瓦片地图 #
瓦片地图概述 #
瓦片地图(Tilemap)是一种高效的地图表示方式,通过重复使用小图像块(瓦片)来构建大型游戏世界。
text
┌─────────────────────────────────────────────────────────────┐
│ 瓦片地图结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Tilemap │
│ ├── Tileset(瓦片集) │
│ │ ├── 瓦片图像 │
│ │ └── 瓦片属性 │
│ ├── Layer(图层) │
│ │ ├── Tile Layer(瓦片图层) │
│ │ └── Object Layer(对象图层) │
│ └── Properties(属性) │
│ │
└─────────────────────────────────────────────────────────────┘
Tiled 地图编辑器 #
安装 Tiled #
Tiled 是一个免费、开源的地图编辑器,支持多种格式导出。
创建地图 #
- 新建地图:File → New → New Map
- 设置地图属性:
- Orientation:Orthogonal(正交)/ Isometric(等角)
- Tile size:瓦片尺寸(如 32x32)
- Map size:地图大小(行列数)
创建瓦片集 #
- 新建瓦片集:File → New → New Tileset
- 导入瓦片图像
- 设置瓦片属性(碰撞、动画等)
图层类型 #
text
┌─────────────────────────────────────────────────────────────┐
│ 图层类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Tile Layer(瓦片图层) │
│ ├── 绘制瓦片 │
│ ├── 地面、墙壁、装饰 │
│ └── 支持多图层叠加 │
│ │
│ Object Layer(对象图层) │
│ ├── 放置对象 │
│ ├── 敌人生成点、触发区域 │
│ └── 自定义属性 │
│ │
│ Image Layer(图像图层) │
│ ├── 背景图像 │
│ └── 视差滚动 │
│ │
└─────────────────────────────────────────────────────────────┘
加载地图 #
导出格式 #
Tiled 支持多种导出格式,Phaser 推荐使用 JSON 格式:
- File → Export As
- 选择 JSON 格式
- 保存为
.json文件
加载资源 #
javascript
preload() {
this.load.tilemapTiledJSON('map', 'assets/map.json');
this.load.image('tiles', 'assets/tiles.png');
this.load.image('background', 'assets/background.png');
this.load.spritesheet('player', 'assets/player.png', {
frameWidth: 32,
frameHeight: 32
});
}
创建地图 #
javascript
create() {
const map = this.make.tilemap({ key: 'map' });
const tileset = map.addTilesetImage('tiles', 'tiles');
const groundLayer = map.createLayer('Ground', tileset);
const wallsLayer = map.createLayer('Walls', tileset);
const decorationLayer = map.createLayer('Decoration', tileset);
}
多瓦片集 #
javascript
create() {
const map = this.make.tilemap({ key: 'map' });
const terrainTiles = map.addTilesetImage('terrain', 'terrain');
const buildingTiles = map.addTilesetImage('buildings', 'buildings');
const propsTiles = map.addTilesetImage('props', 'props');
const groundLayer = map.createLayer('Ground', [terrainTiles, buildingTiles]);
const propsLayer = map.createLayer('Props', propsTiles);
}
图层操作 #
获取图层 #
javascript
const map = this.make.tilemap({ key: 'map' });
const layer = map.getLayer('Ground');
const layer = map.getLayerAt(0);
const layers = map.layers;
const layerNames = map.getLayerNames();
创建图层 #
javascript
const map = this.make.tilemap({ key: 'map' });
const tileset = map.addTilesetImage('tiles', 'tiles');
const layer = map.createLayer('Ground', tileset);
const layer = map.createLayer('Ground', tileset, x, y);
const layer = map.createLayer(0, tileset);
const layer = map.createBlankLayer('NewLayer', tileset);
图层属性 #
javascript
layer.alpha = 0.5;
layer.setVisible(false);
layer.setDepth(10);
layer.setScale(2, 2);
layer.setPosition(100, 100);
layer.setScrollFactor(0.5, 0.5);
layer.setCullPadding(2, 2);
layer.skipCull = true;
图层碰撞 #
javascript
const map = this.make.tilemap({ key: 'map' });
const tileset = map.addTilesetImage('tiles', 'tiles');
const wallsLayer = map.createLayer('Walls', tileset);
wallsLayer.setCollisionByProperty({ collides: true });
wallsLayer.setCollisionBetween(1, 100);
wallsLayer.setCollision([1, 2, 3, 4, 5]);
wallsLayer.setCollisionByExclusion([-1, 0]);
this.physics.add.collider(this.player, wallsLayer);
瓦片操作 #
javascript
const tile = layer.getTileAt(5, 5);
const tile = layer.getTileAtWorldXY(200, 200);
const tiles = layer.getTilesWithin(0, 0, 10, 10);
layer.putTileAt(10, 5, 5);
layer.putTileAtWorldXY(10, 200, 200);
layer.removeTileAt(5, 5);
layer.fill(10, 0, 0, 10, 10);
layer.randomize(0, 0, 10, 10, [1, 2, 3, 4, 5]);
layer.weightedRandomize(0, 0, 10, 10, [
{ index: 1, weight: 4 },
{ index: 2, weight: 1 }
]);
layer.replaceByIndex(1, 10);
瓦片属性 #
javascript
const tile = layer.getTileAt(5, 5);
console.log(tile.index);
console.log(tile.x, tile.y);
console.log(tile.pixelX, tile.pixelY);
console.log(tile.properties);
console.log(tile.rotation);
console.log(tile.flipX, tile.flipY);
console.log(tile.alpha);
console.log(tile.tint);
if (tile.properties.collides) {
console.log('This tile collides');
}
对象层 #
解析对象层 #
javascript
const map = this.make.tilemap({ key: 'map' });
const spawnPoint = map.findObject('Objects', obj => obj.name === 'Spawn Point');
this.player = this.physics.add.sprite(spawnPoint.x, spawnPoint.y, 'player');
const objects = map.filterObjects('Objects', obj => obj.type === 'enemy');
objects.forEach(obj => {
this.enemies.create(obj.x, obj.y, 'enemy');
});
const objectLayer = map.getObjectLayer('Objects');
objectLayer.objects.forEach(obj => {
console.log(obj.name, obj.type, obj.x, obj.y);
});
对象属性 #
javascript
const map = this.make.tilemap({ key: 'map' });
const objects = map.getObjectLayer('Objects').objects;
objects.forEach(obj => {
console.log('Name:', obj.name);
console.log('Type:', obj.type);
console.log('Position:', obj.x, obj.y);
console.log('Size:', obj.width, obj.height);
console.log('Rotation:', obj.rotation);
console.log('Visible:', obj.visible);
console.log('Properties:', obj.properties);
if (obj.properties) {
const health = obj.properties.find(p => p.name === 'health');
if (health) {
console.log('Health:', health.value);
}
}
});
创建对象 #
javascript
const map = this.make.tilemap({ key: 'map' });
const enemies = map.filterObjects('Objects', obj => obj.type === 'enemy');
enemies.forEach(obj => {
const enemy = this.physics.add.sprite(obj.x, obj.y, 'enemy');
if (obj.properties) {
const speed = obj.properties.find(p => p.name === 'speed');
if (speed) {
enemy.speed = speed.value;
}
}
this.enemyGroup.add(enemy);
});
const triggers = 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);
zone.body.setAllowGravity(false);
zone.body.moves = false;
this.physics.add.overlap(this.player, zone, () => {
console.log('Triggered:', obj.name);
});
});
相机跟随 #
世界边界 #
javascript
const map = this.make.tilemap({ key: 'map' });
this.physics.world.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.player.setCollideWorldBounds(true);
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.startFollow(this.player, true, 0.08, 0.08);
this.cameras.main.startFollow(this.player, true, 0.1, 0.1, 0, 0);
视差滚动 #
javascript
const map = this.make.tilemap({ key: 'map' });
const backgroundLayer = map.createLayer('Background', tileset);
backgroundLayer.setScrollFactor(0.1);
const midgroundLayer = map.createLayer('Midground', tileset);
midgroundLayer.setScrollFactor(0.5);
const foregroundLayer = map.createLayer('Foreground', tileset);
foregroundLayer.setScrollFactor(1.2);
动态地图 #
动态瓦片 #
javascript
update() {
const tile = this.groundLayer.getTileAtWorldXY(this.player.x, this.player.y + 32);
if (tile && tile.index === 10) {
this.groundLayer.putTileAtWorldXY(11, this.player.x, this.player.y + 32);
}
}
breakTile(x, y) {
const tile = this.groundLayer.getTileAtWorldXY(x, y);
if (tile && tile.properties.breakable) {
this.groundLayer.removeTileAtWorldXY(x, y);
this.createDebris(x, y);
this.sound.play('break');
}
}
动态碰撞 #
javascript
updateMap() {
this.collisionLayer.forEachTile(tile => {
if (tile.index === 10) {
tile.setCollision(true);
}
});
}
地图转换 #
世界坐标与瓦片坐标 #
javascript
const map = this.make.tilemap({ key: 'map' });
const tileX = map.worldToTileX(worldX);
const tileY = map.worldToTileY(worldY);
const worldX = map.tileToWorldX(tileX);
const worldY = map.tileToWorldY(tileY);
const tile = map.getTileAt(tileX, tileY, true, 'Ground');
路径查找 #
javascript
findPath(startX, startY, endX, endY) {
const map = this.make.tilemap({ key: 'map' });
const startTileX = map.worldToTileX(startX);
const startTileY = map.worldToTileY(startY);
const endTileX = map.worldToTileX(endX);
const endTileY = map.worldToTileY(endY);
const path = [];
return path;
}
完整示例 #
平台游戏地图 #
javascript
class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' });
}
preload() {
this.load.tilemapTiledJSON('map', 'assets/map.json');
this.load.image('tiles', 'assets/tiles.png');
this.load.spritesheet('player', 'assets/player.png', {
frameWidth: 32,
frameHeight: 48
});
}
create() {
const map = this.make.tilemap({ key: 'map' });
const tileset = map.addTilesetImage('tiles', 'tiles');
const backgroundLayer = map.createLayer('Background', tileset);
backgroundLayer.setScrollFactor(0.5);
const groundLayer = map.createLayer('Ground', tileset);
groundLayer.setCollisionByProperty({ collides: true });
const decorationLayer = map.createLayer('Decoration', tileset);
const spawnPoint = map.findObject('Objects', obj => obj.name === 'Spawn Point');
this.player = this.physics.add.sprite(spawnPoint.x, spawnPoint.y, 'player');
this.player.setCollideWorldBounds(true);
this.physics.add.collider(this.player, groundLayer);
this.physics.world.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.startFollow(this.player, true, 0.08, 0.08);
this.coins = this.physics.add.group();
const coinObjects = map.filterObjects('Objects', obj => obj.type === 'coin');
coinObjects.forEach(obj => {
const coin = this.coins.create(obj.x + 16, obj.y + 16, 'coin');
coin.body.setAllowGravity(false);
});
this.physics.add.overlap(this.player, this.coins, this.collectCoin, null, this);
this.cursors = this.input.keyboard.createCursorKeys();
}
update() {
if (this.cursors.left.isDown) {
this.player.setVelocityX(-160);
} else if (this.cursors.right.isDown) {
this.player.setVelocityX(160);
} else {
this.player.setVelocityX(0);
}
if (this.cursors.up.isDown && this.player.body.blocked.down) {
this.player.setVelocityY(-330);
}
}
collectCoin(player, coin) {
coin.destroy();
this.score += 10;
}
}
下一步 #
现在你已经掌握了瓦片地图,接下来学习 音频系统,了解如何为游戏添加音效和背景音乐!
最后更新:2026-03-29