You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Phaser 3单图层多Tileset使用putTileAt的渲染与报错问题

Phaser 3 单图层多Tileset渲染异常问题

我正在用Phaser 3开发个人项目,之前用多个图层处理不同Tileset的瓦片,效果正常。但看到Phaser支持单图层使用多个Tileset,尝试合并成单图层后遇到了问题:放置指定Tileset的瓦片时,始终只会从第一个Tileset中提取对应索引的精灵。

将所有瓦片按网格排列后,出现如下渲染结果:
所有瓦片重复显示第一个Tileset的图像
可以看到,虽然各Tileset的长度设置正确,但第一个精灵表被重复显示——先是第一个表的350个瓦片,接着是第二个表的约318个、第三个的60个、第四个的30个,全部显示第一个精灵表的内容。

初始实现代码

initTileSets() {
    // Initialize tilemap
    this.bgTilemap = this.scene.make.tilemap({
        width: this.mapWidth,
        height: this.mapHeight,
        tileWidth: this.tileSize,
        tileHeight: this.tileSize
    });

    // Add all tilesets to the tilemap
    this.terrainTiles = this.bgTilemap.addTilesetImage('terrainTileset');
    this.terrainTiles2 = this.bgTilemap.addTilesetImage('terrainTileset2');
    this.rogueTiles = this.bgTilemap.addTilesetImage('rogueTileset');
    this.snowTiles = this.bgTilemap.addTilesetImage('snowTileset');

    //Set the length of each tileset
    let terrainTilesetLength = this.terrainTiles.total;
    let terrainTileset2Length = this.terrainTiles2.total;
    let rogueTilesetLength = this.rogueTiles.total;
    let snowTilesetLength = this.snowTiles.total;

    //Set the first gid of each tileset to the total of the previous tileset
    this.terrainTiles.firstgid = 0;
    this.terrainTiles2.firstgid = terrainTilesetLength;
    this.rogueTiles.firstgid = terrainTilesetLength + terrainTileset2Length;
    this.snowTiles.firstgid = terrainTilesetLength + terrainTileset2Length + rogueTilesetLength;

    // Store tileset information
    this.tilesetsInfo = [
        { name: 'terrainTileset', firstgid: this.terrainTiles.firstgid },
        { name: 'terrainTileset2', firstgid: this.terrainTiles2.firstgid },
        { name: 'rogueTileset', firstgid: this.rogueTiles.firstgid },
        { name: 'snowTileset', firstgid: this.snowTiles.firstgid }
    ];

    let allTilesets = [
        this.terrainTiles,
        this.terrainTiles2,
        this.rogueTiles,
        this.snowTiles
    ];

    // Create two main layers
    this.terrainLayer = this.bgTilemap.createBlankLayer('terrainLayer', allTilesets, 0, 0);

    this.detailLayer = this.bgTilemap.createBlankLayer('detailLayer', allTilesets, 0, 0);

    //log detaillayer
    console.log(this.detailLayer);

    // Optionally, you can manage all layers together if you need to apply settings to all
    this.allLayers = [
        this.terrainLayer,
        this.detailLayer
    ];
}

getTilesetForIndex(globalIndex) 
{
    for (let i = this.tilesetsInfo.length - 1; i >= 0; i--) 
    {
        let tileset = this.tilesetsInfo[i];
        if (globalIndex >= tileset.firstgid) 
        {
            return this.bgTilemap.getTileset(tileset.name);
        }
    }
    return null;
}

placeTile(x, y, globalIndex, layer) 
{
    let tileset = this.getTilesetForIndex(globalIndex);
    if (!tileset) 
    {
        console.error(`Tileset not found for global index: ${globalIndex}`);
        return;
    }

    //log name of tileset, first gid, and global index
    //console.log('tileset: ' + tileset.name + ' | firstgid: ' + tileset.firstgid + ' | globalIndex: ' + globalIndex);
    // Calculate the local tile index within the selected tileset
    let localIndex = globalIndex - tileset.firstgid;

    // Place the tile
    layer.putTileAt(localIndex, x, y, false, tileset.name);
}

getTotalTilesInLayer(layer) 
{
    let totalTiles = 0;
    layer.tileset.forEach(tileset => {
        totalTiles += tileset.total;
    });
    return totalTiles;
}

placeDebugTileLine()
{
    for (let i = 0; i < this.getTotalTilesInLayer(this.detailLayer); i++) 
    {
        let row = Math.floor(i/25);
        let col = i % 25;
        
        this.placeTile(col, row, i, this.detailLayer);
    }
}

我注意到placeTile()方法中,layer.putTileAt似乎忽略了最后一个tileset名称参数——即使传入无效的Tileset名称也能正常运行,这和文档描述不符。


更新后的问题

修改代码后,当map.putTileAt循环执行到i=350(第一个Tileset的长度上限)时,出现未定义错误:
错误日志

修改后的代码

initTileSets2()
{
    let map = this.scene.make.tilemap({
        key:'map',
        width: 16,
        height: 16,
        tileWidth: 16,
        tileHeight: 16
    });

    // Add all tilesets to the tilemap
    let tiles1 = map.addTilesetImage('terrainTileset'); // 350 tiles
    let tiles2 = map.addTilesetImage('terrainTileset2'); // 324 tiles
    let tiles3 = map.addTilesetImage('rogueTileset'); // 60 tiles
    let tiles4 = map.addTilesetImage('snowTileset'); // 30 tiles
    
    //Set the first gid of each tileset to the total of the previous tileset
    tiles1.firstgid = 0;
    tiles2.firstgid = tiles1.total;
    tiles3.firstgid = tiles2.firstgid + tiles2.total;
    tiles4.firstgid = tiles3.firstgid + tiles3.total;

    let allTilesets = [
        tiles1,
        tiles2,
        tiles3,
        tiles4
    ];

    let layer1 = map.createBlankLayer('layer1', allTilesets, 20, 50).setScale(0.75);
        
    let tilePerRow = 25;
    for (let i = 0; i < tiles4.firstgid; i++) {
        let col = i % tilePerRow;
        let row  = Math.floor( i / tilePerRow );

        console.log('i: ' + i + ' | col: ' + col + ' | row: ' + row);
        map.putTileAt( i, col, row )
    }

    this.map = map;
}

奇怪现象

如果不设置各Tileset的firstgid,且硬编码让循环在i=350时停止,会出现如下渲染结果:
各Tileset从索引0堆叠渲染
这说明所有瓦片都已加载到地图中,但一旦设置firstgid避免堆叠,就会回到最初的重复渲染第一个Tileset的问题。


内容的提问来源于stack exchange,提问作者Daniel Dees

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:37:01