О чем этот пример

При создании тайловых карт в Phaser иногда возникает ситуация, когда тайлы отображаются со смещением, нарушая выравнивание карты. Эта статья на примере реального кода из issue #4391 объясняет, почему возникает проблема и как её решить, корректно настроив параметры `addTilesetImage` и используя правильный метод для создания слоя.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    pixelArt: true,
    scene: {
        preload: preload,
        create: create
    }
};

var game = new Phaser.Game(config);

function preload ()
{
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('blocks', 'assets/bugs/BlockMap-extruded.png');
    this.load.spritesheet('smileys', 'assets/bugs/SmileyMap.png', { frameWidth: 26, frameHeight: 26 });
}

function create ()
{
    var data = [[0, 0, 0], [0, 0, 0], [9, 9, 9]];

    var map = this.make.tilemap({ data, tileWidth: 16, tileHeight: 16 });

    var tileset = map.addTilesetImage('blocks', null, 16, 16, 1, 2);

    // var tileset = map.addTilesetImage('blocks', null, 16, 16, 0, 0);

    // map.createLayer(0, tileset, 0, 0);

    map.createStaticLayer(0, tileset, 0, 0);

    var player = this.add.sprite(0, 0.5, 'smileys', 0);

    this.cameras.main.startFollow(player);
}

Проблема: тайлы отображаются не на своих местах

В исходном примере используется простая тайловая карта, созданная из двумерного массива данных. Цель — отобразить три строки тайлов, где последняя строка должна состоять из трёх одинаковых тайлов (индекс 9). Однако при запуске кода тайлы могут отображаться со смещением, особенно заметным при использовании текстур с внутренними отступами (padding) или маской (margin).

Ключевая проблема кроется в параметрах, передаваемых в метод addTilesetImage. Если они не соответствуют реальной структуре файла tileset-изображения, Phaser не сможет правильно нарезать текстуру на отдельные тайлы, что приводит к визуальным артефактам.

Анализ параметров addTilesetImage

Метод map.addTilesetImage связывает загруженное изображение с данными тайловой карты. Его сигнатура для нашего случая выглядит так:

map.addTilesetImage(key, tilesetName, tileWidth, tileHeight, tileMargin, tileSpacing)

В исходном коде используется вызов:

var tileset = map.addTilesetImage('blocks', null, 16, 16, 1, 2);

Здесь tileMargin: 1 и tileSpacing: 2. Параметр tileMargin определяет отступ (в пикселях) от края изображения до первого тайла. Параметр tileSpacing — это расстояние между тайлами внутри tileset-изображения. Если эти значения не соответствуют реальному файлу 'blocks', нарезка происходит неправильно.

Закомментированная строка предлагает альтернативу с нулевыми значениями:

// var tileset = map.addTilesetImage('blocks', null, 16, 16, 0, 0);

Для большинства стандартных tileset-изображений, где тайлы расположены вплотную друг к другу и начинаются с самого края, параметры 0, 0 будут корректными. В нашем случае файл BlockMap-extruded.png, судя по названию, вероятно, содержит экструдированные тайлы (с расширенными краями для устранения артефактов при фильтрации), что может требовать других значений margin/spacing. Необходимо свериться с тем, как реально построено это изображение.

Создание слоя: createStaticLayer vs createLayer

Ещё один важный момент — выбор метода для создания отображаемого слоя. В исходном коде используется:

map.createStaticLayer(0, tileset, 0, 0);

Однако есть закомментированная альтернатива:

// map.createLayer(0, tileset, 0, 0);

Разница существенна. Метод createLayer является устаревшим (deprecated) в более новых версиях Phaser 3. Он создаёт Phaser.Tilemaps.DynamicTilemapLayer. Метод createStaticLayer создаёт Phaser.Tilemaps.StaticTilemapLayer, который оптимизирован для слоёв, тайлы на которых не изменяются в процессе выполнения игры (не анимируются, не перезаписываются). Для статичной карты, как в нашем примере, createStaticLayer — более правильный и производительный выбор.

Оба метода принимают индекс слоя данных (в нашем случае это `0`), tileset или массив tilesets, и координаты X, Y для позиционирования слоя на сцене.

Корректное решение

Основываясь на анализе, для исправления смещения нужно: 1. Убедиться, что параметры tileMargin и tileSpacing в addTilesetImage соответствуют реальному tileset-изображению. 2. Использовать актуальный метод createStaticLayer.

Скорректированный код функции create может выглядеть так:

function create ()
{
    var data = [[0, 0, 0], [0, 0, 0], [9, 9, 9]];

    var map = this.make.tilemap({ data, tileWidth: 16, tileHeight: 16 });

    // Предполагаем, что в файле blocks.png тайлы идут без отступов и промежутков
    var tileset = map.addTilesetImage('blocks', null, 16, 16, 0, 0);

    // Создаём статичный слой
    map.createStaticLayer(0, tileset, 0, 0);

    var player = this.add.sprite(0, 0.5, 'smileys', 0);
    this.cameras.main.startFollow(player);
}

Если проблема со смещением сохраняется, необходимо проверить исходное изображение blocks. Возможно, его тайлы имеют размер не 16x16, или оно содержит внутренние отступы. В этом случае значения tileMargin и tileSpacing нужно подбирать экспериментально или уточнять у художника, создавшего tileset.

Что попробовать дальше

Смещение тайлов в Phaser чаще всего возникает из-за несоответствия параметров tileMargin и tileSpacing в addTilesetImage реальной структуре изображения tileset'а. Решение — тщательно проверить эти значения и использовать современный API, такой как createStaticLayer для статичных карт. Для экспериментов попробуйте

  1. Визуально проанализировать tileset-изображение в графическом редакторе, чтобы точно замерить отступы
  2. Поиграть с параметрами tileMargin и tileSpacing в реальном времени, выводя их значения на экран
  3. Проверить, решает ли проблему использование другого tileset'а с известными параметрами