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

При создании 2D-игр в стиле pixel art разработчики часто сталкиваются с визуальными артефактами — 'протеканием' (bleeding) текстур тайлов при скроллинге камеры. Эта проблема портит целостность картины, делая линии размытыми и неровными. В статье разберем готовый тестовый пример из Phaser, который наглядно демонстрирует эту проблему и показывает её изящное решение через правильную настройку рендеринга. Вы узнаете, какие ключевые настройки конфигурации и методы API необходимы для чистого отображения тайловых карт.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('tiles', 'assets/tilemaps/tiles/catastrophi_tiles_16.png');
        this.load.tilemapCSV('map', 'assets/tilemaps/csv/catastrophi_level2.csv');
    }

    create ()
    {
        const map = this.make.tilemap({ key: 'map', tileWidth: 16, tileHeight: 16 });
        const tileset = map.addTilesetImage('tiles');
        const layer = map.createLayer(0, tileset, 0, 0);

        // Visual test to make sure tiles don't bleed while scrolling at certain speeds
    }

    update (time, delta)
    {
        this.cameras.main.scrollX = (200 + Math.cos(time / 1000) * 200);
        this.cameras.main.scrollY = (500 + Math.sin(time / 1000) * 500);
    }
}

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    pixelArt: true,
    scene: Example
};

const game = new Phaser.Game(config);

Суть проблемы: почему текстуры 'протекают'

Артефакт 'протекания' возникает, когда графический процессор при отрисовке текселя (пикселя текстуры) на границе тайла для антиалиасинга берёт данные из соседнего тайла. Это особенно заметно в pixel art, где каждый пиксель важен, и при скроллинге камеры с дробными (нецелыми) координатами появляются цветные полосы между тайлами.

Пример в статье специально создан для визуальной проверки (Visual test), чтобы убедиться, что тайлы не 'протекают' при скроллинге с определёнными скоростями. Камера движется по сложной траектории, что увеличивает вероятность появления артефактов.

Анализ кода: загрузка и создание тайловой карты

Всё начинается с метода preload. Здесь загружаются необходимые ресурсы: тайлсет (изображение с тайлами) и сама карта уровня в формате CSV.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/tiles/catastrophi_tiles_16.png');
this.load.tilemapCSV('map', 'assets/tilemaps/csv/catastrophi_level2.csv');

В методе create происходит основная работа по созданию тайловой карты. Ключевые шаги: 1. Создание объекта Tilemap из загруженных CSV-данных с указанием размера тайла. 2. Связывание тайлсета (изображения) с картой. 3. Создание слоя для отрисовки.

const map = this.make.tilemap({ key: 'map', tileWidth: 16, tileHeight: 16 });
const tileset = map.addTilesetImage('tiles');
const layer = map.createLayer(0, tileset, 0, 0);

Движение камеры и симуляция проблемы

Чтобы проверить отображение тайлов в динамике, в методе update реализовано плавное движение камеры по синусоидальной траектории. Это приводит к постоянному изменению её координат scrollX и scrollY, часто на дробные значения.

this.cameras.main.scrollX = (200 + Math.cos(time / 1000) * 200);
this.cameras.main.scrollY = (500 + Math.sin(time / 1000) * 500);

Именно такое движение с дробными координатами и является триггером для появления артефактов 'протекания', если рендеринг настроен неправильно.

Ключевое решение: настройка `pixelArt` в конфиге

Самый важный момент, который предотвращает проблему в данном примере, — это настройка pixelArt: true в объекте конфигурации игры (config).

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    pixelArt: true, // Критически важная настройка для pixel art
    scene: Example
};

Что делает эта настройка на низком уровне: 1. **Отключает линейную фильтрацию текстур** (LINEAR) и включает точечную (NEAREST). Это гарантирует, что при масштабировании или смещении пиксели остаются чёткими, без размытия. 2. **Автоматически округляет координаты отрисовки** (roundPixels: true) на уровне камеры. Это предотвращает смещение на долю пикселя, которое и вызывает 'протекание'. Без этого даже с точечной фильтрацией могут появляться артефакты при скроллинге.

Использование this.make.tilemap вместо this.add.tilemap также может играть роль, так как фабричный метод make лучше интегрируется с внутренними системами управления текстурами для pixel art.

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

Для чистого отображения pixel art тайловых карт в Phaser при динамическом скроллинге достаточно одной ключевой настройки в конфигурации игры — pixelArt: true. Она комплексно решает проблемы фильтрации и округления координат. Для экспериментов попробуйте установить pixelArt: false в примере и увидите появление артефактов. Также можно поэкспериментировать с ручным управлением свойствами roundPixels у камеры или antialias в настройках рендерера, чтобы глубже понять их взаимодействие.