О чем этот пример
При создании тайловых карт в 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 для статичных карт. Для экспериментов попробуйте
- Визуально проанализировать tileset-изображение в графическом редакторе, чтобы точно замерить отступы
- Поиграть с параметрами
tileMarginиtileSpacingв реальном времени, выводя их значения на экран - Проверить, решает ли проблему использование другого tileset'а с известными параметрами
