用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/feliperyba/ralph-orchestra --skill dev-phaser-tilemaps命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Complete Developer workflow orchestration - task research sequence, implementation flow, validation gates, PRD synchronization, exit conditions.
Complete Game Designer workflow - skill invocation protocol, GDD creation, playtest flow with GDD review, design sessions. MUST load before starting assignments.
Complete PM Coordinator workflow - task assignment, project orchestration, PRD management, worker coordination. Use proactively when starting PM agent work.
正在显示 SKILL.md
基于 SOC 职业分类
| name | dev-phaser-tilemaps |
| description | Tilemap loading, parsing, layers, and collision detection |
"Build expansive worlds with tile-based maps from Tiled."
// Manual tilemap without Tiled integration
interface Tile {
x: number;
y: number;
type: string;
solid: boolean;
}
interface Level {
tiles: Tile[];
width: number;
height: number;
spawn: { x: number; y: number };
}
// Hardcoded level data
const level1: Level = {
tiles: [
{ x: 0, y: 0, type: 'grass', solid: false },
{ x: 1, y: 0, type: 'grass', solid: false },
{ x: 2, y: 0, type: 'wall', solid: true },
// ... hundreds more entries
],
width: 50,
height: 50,
spawn: { x: 100, y: 100 }
};
class LevelRenderer {
private tileImages: Map<string, HTMLImageElement> = new Map();
constructor(private canvas: HTMLCanvasElement) {
// Load tile images manually
this.loadTile('grass', 'assets/tiles/grass.png');
this.loadTile('wall', 'assets/tiles/wall.png');
this.loadTile('water', 'assets/tiles/water.png');
}
async loadTile(type: string, path: string) {
const img = new Image();
img.src = path;
await new Promise(resolve => img.onload = resolve);
this.tileImages.set(type, img);
}
render(level: Level, cameraX: number, cameraY: number) {
const ctx = this.canvas.getContext('2d')!;
// Manual culling - only draw visible tiles
for (const tile of level.tiles) {
const screenX = tile.x * 32 - cameraX;
const screenY = tile.y * 32 - cameraY;
// Culling check
if (screenX < -32 || screenX > this.canvas.width ||
screenY < -32 || screenY > this.canvas.height) {
continue;
}
const img = this.tileImages.get(tile.type);
if (img) {
ctx.drawImage(img, screenX, screenY);
}
}
}
// Manual collision detection
getTileAt(x: number, y: number, level: Level): Tile | null {
const tileX = Math.floor(x / 32);
const tileY = Math.floor(y / 32);
return level.tiles.find(t => t.x === tileX && t.y === tileY) || null;
}
isSolid(x: number, y: number, level: Level): boolean {
const tile = this.getTileAt(x, y, level);
return tile?.solid || false;
}
}
// Problems:
// - Hardcoded level data is unmaintainable
// - No visual editor for level design
// - Manual tile culling is error-prone
// - No layer support (parallax, foreground)
// - Manual collision detection for each tile
// - No object layer support (spawn points, enemies)
// - Hard to add new tiles or change layouts
// Tiled + Phaser handles everything automatically
export class GameScene extends Phaser.Scene {
private map!: Phaser.Tilemaps.Tilemap;
preload() {
// Load Tiled JSON export - ONE line!
this.load.tilemapTiledJSON('dungeon', 'assets/tilemaps/dungeon.json');
this.load.image('dungeon_tiles', 'assets/tilesets/dungeon.png');
}
create() {
// Create tilemap from Tiled - ONE line!
this.map = this.make.tilemap({ key: 'dungeon' });
// Add tileset - matches Tiled project name!
const tileset = this.map.addTilesetImage('Dungeon Tiles', 'dungeon_tiles');
// Create layers automatically from Tiled layers!
const bgLayer = this.map.createLayer('Background', tileset);
const wallsLayer = this..(, tileset);
fgLayer = ..(, tileset);
wallsLayer.({ : });
...(., wallsLayer);
spawnPoint = ..(, obj. === );
..(spawnPoint., spawnPoint.);
..(, , {
: ,
:
});
bgLayer.();
wallsLayer.();
fgLayer.();
...(
, ,
..,
..
);
}
}
Use when:
preload() {
this.load.tilemapTiledJSON('map', 'assets/level1.json');
this.load.image('tiles', 'assets/tileset.png');
}
create() {
const map = this.make.tilemap({ key: 'map' });
const tileset = map.addTilesetImage('ground', 'tiles');
const layer = map.createLayer('Ground', tileset);
layer.setCollisionByExclusion([-1]);
}
| Need | Use |
|---|---|
| Tiled JSON export | load.tilemapTiledJSON() |
| Collision layer | setCollisionByExclusion([-1]) |
| Tile properties | setTileIndexCallback() |
| Isometric view | Tiled isometric orientation |
| Object layer | map.createFromObjects() |
export class GameScene extends Phaser.Scene {
private map!: Phaser.Tilemaps.Tilemap;
private tileset!: Phaser.Tilemaps.Tileset;
private groundLayer!: Phaser.Tilemaps.DynamicTilemapLayer;
private wallsLayer!: Phaser.Tilemaps.DynamicTilemapLayer;
preload() {
// Load tilemap JSON (exported from Tiled)
this.load.tilemapTiledJSON("dungeon", "assets/tilemaps/dungeon.json");
// Load tileset image
this.load.image("dungeon_tiles", "assets/tilesets/dungeon.png");
}
create() {
// Create tilemap
this.map = this.make.tilemap({ key: "dungeon" });
// Add tileset image (first param: name in Tiled, second: image key)
this.tileset = this..(, );
. = ..(, .);
. = ..(, .);
}
}
create() {
this.map = this.make.tilemap({ key: 'dungeon' });
this.tileset = this.map.addTilesetImage('Dungeon Tiles', 'dungeon_tiles');
// Create layer
const walls = this.map.createLayer('Walls', this.tileset);
// Set collision for ALL tiles
walls.setCollisionByProperty({ collides: true });
// Alternative: Set collision by exclusion (all except index -1)
walls.setCollisionByExclusion([-1]);
// Set collision for specific tile indices
walls.setCollision([1, 2, 3, 4]);
// Create player
const player = this.physics.add.sprite(100, 100, 'player');
player.setCollideWorldBounds(true);
// Add collision between player and tilemap layer
...(player, walls);
}
create() {
this.map = this.make.tilemap({ key: 'level' });
this.tileset = this.map.addTilesetImage('tiles', 'tiles');
const worldLayer = this.map.createLayer('World', this.tileset);
// Set collision from tile properties
worldLayer.setCollisionByProperty({ collides: true });
// Tile index callback (trigger when tile touched)
worldLayer.setTileIndexCallback(
5, // Tile index
this.lavaCallback,
this
);
// Tile location callback
worldLayer.setTileLocationCallback(
10, 10, // x, y
5, 5, // width, height
this.coinCallback,
this
);
}
lavaCallback(sprite: Phaser.Types..., : ..) {
player = sprite ...;
.(player);
}
() {
layer = tile.;
layer.(tile., tile.);
. += ;
}
create() {
this.map = this.make.tilemap({ key: 'level' });
this.tileset = this.map.addTilesetImage('tiles', 'tiles');
// Create from object layer
const objects = this.map.getObjectLayer('Objects');
if (objects) {
// Create sprites from objects
objects.objects.forEach((obj: any) => {
if (obj.type === 'player') {
this.spawnPlayer(obj.x, obj.y);
} else if (obj.type === 'enemy') {
this.spawnEnemy(obj.x, obj.y, obj.properties);
} else if (obj.type === 'coin') {
this.spawnCoin(obj., obj.);
}
});
}
coins = ..(, , {
: ,
:
});
...(coins);
}
export class GameScene extends Phaser.Scene {
private map!: Phaser.Tilemaps.Tilemap;
private layers: Phaser.Tilemaps.DynamicTilemapLayer[] = [];
create() {
this.map = this.make.tilemap({ key: "large_level" });
this.tileset = this.map.addTilesetImage("tiles", "tiles");
// Multiple layers
const bgLayer = this.map.createLayer("Background", this.tileset);
const decorLayer = this.map.createLayer("Decoration", this.tileset);
const mainLayer = this.map.createLayer("Main", this.tileset);
fgLayer = ..(, .);
bgLayer.();
decorLayer.();
mainLayer.();
fgLayer.();
mainLayer.({ : });
fgLayer.({ : });
player = ...(, , );
...(player, mainLayer);
...(player, fgLayer);
.(mainLayer, player);
...(
,
,
..,
..,
);
}
() {
..(, {
tile = layer.(pointer., pointer.);
(tile) {
.(, tile., , tile.);
(tile..) {
layer.(tile., tile.);
.(tile., tile.);
}
}
});
animatedTiles = [{ : , : [, , ], : }];
animatedTiles.( {
..[anim.] = {
: anim..( ({
frame,
: anim.,
})),
};
});
}
(
: ,
: ,
: ,
: ,
): {
layer = .[];
line = ..(fromX, fromY, toX, toY);
tiles = layer.(line);
!tiles.( tile..);
}
() {
layer = .[layerIndex];
( ty = y; ty < y + h; ty++) {
( tx = x; tx < x + w; tx++) {
layer.(tileIndex, tx, ty);
}
}
}
}
❌ DON'T:
✅ DO:
tile.pixelX/Y for pixel positionsIn Tiled, set tile properties:
- collides: true
- dangerous: true
- animates: true
- frameRate: 5
// Access properties
layer.setCollisionByProperty({ collides: true });
// Custom property callback
layer.setTileLocationCallback(
0,
0,
map.width,
map.height,
(sprite, tile) => {
if (tile.properties.dangerous) {
this.hurtPlayer(sprite);
}
},
this,
);
create() {
const bgLayer = map.createLayer('Background', tileset);
const midLayer = map.createLayer('Midground', tileset);
const fgLayer = map.createLayer('Foreground', tileset);
// Parallax scrolling
bgLayer.setScrollFactor(0.2); // Far background
midLayer.setScrollFactor(0.5); // Mid ground
fgLayer.setScrollFactor(1); // Foreground (normal)
}
create() {
// Set camera bounds to tilemap size
this.cameras.main.setBounds(
0, 0,
this.map.widthInPixels,
this.map.heightInPixels
);
// Set world bounds
this.physics.world.setBounds(
0, 0,
this.map.widthInPixels,
this.map.heightInPixels
);
}
| Method | Description |
|---|---|
createLayer(name, tileset) | Create layer from tilemap |
setCollisionByProperty(props) | Set collision from properties |
setCollisionByExclusion(indices) | Set collision except specified |
setTileIndexCallback(index, fn) | Callback when tile touched |
getTileAt(x, y) | Get tile at grid coordinates |
getTileAtWorldXY(x, y) | Get tile at pixel coordinates |
putTileAt(index, x, y) | Place tile at grid coordinates |
removeTileAt(x, y) | Remove tile |
swapTiles(indexA, indexB) | Swap all tiles of two types |