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

При разработке пиксель-арт игр на Phaser разработчики часто сталкиваются с размытием спрайтов и появлением визуальных артефактов при движении камеры или объектов. Эти проблемы возникают из-за субпиксельного рендеринга, когда координаты объектов оказываются дробными. Встроенные настройки движка позволяют легко решить эту задачу, сохраняя чёткость пиксельной графики. В этой статье мы разберём пример использования конфигурационных параметров `roundPixels` и `pixelArt`, которые контролируют выравнивание координат. Вы узнаете, как эти настройки влияют на рендеринг, и как их правильно применять в своих проектах.

Версия 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('pic', 'assets/pics/atari-fujilogo.png');
        this.load.image('sprite', 'assets/sprites/mushroom16x16.png');
        this.load.image('bik', 'assets/sprites/bikkuriman.png');
    }

    create ()
    {
        const a = this.add.image(400, 150, 'bik');

        const b = this.add.image(400, 300, 'pic');

        const c = this.add.image(100, 300, 'sprite').setScale(10);

        const d = this.add.image(700, 300, 'sprite').setScale(10);

        let aligned = true;

        const left = this.add.text(10, 10, 'Left 0.5px', { font: '16px Courier' });
        const right = this.add.text(680, 10, 'Right 0.5px', { font: '16px Courier' });

        left.setInteractive();
        right.setInteractive();

        left.on('pointerdown', () => {

            this.cameras.main.zoom += 1;

            console.log(this.cameras.main.zoom);

            // this.cameras.main.scrollX += 0.5;
            // console.log(this.cameras.main.scrollX);

        });

        right.on('pointerdown', () => {

            this.cameras.main.zoom -= 0.5;

            console.log(this.cameras.main.zoom);

            // this.cameras.main.scrollX -= 0.5;
            // console.log(this.cameras.main.scrollX);

        });

        this.input.on('pointerdown', (pointer) => {

            if (pointer.y < 200)
            {
                return;
            }

            aligned = !aligned;

            a.x += (aligned) ? 0.5 : -0.5;
            b.x += (aligned) ? 0.5 : -0.5;
            c.x += (aligned) ? 0.5 : -0.5;
            d.x += (aligned) ? 0.5 : -0.5;

            a.y += (aligned) ? 0.25 : -0.25;
            b.y += (aligned) ? 0.25 : -0.25;
            c.y += (aligned) ? 0.25 : -0.25;
            d.y += (aligned) ? 0.25 : -0.25;

        });

    }
}

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

const game = new Phaser.Game(config);

Проблема: субпиксельное смещение и размытие

При перемещении игровых объектов на дробные значения (например, на 0.5 пикселя) браузер пытается отрисовать их в промежуточных позициях. Для пиксельной графики это приводит к размытию и нечётким границам, так как пиксели спрайта "растягиваются" между соседними пикселями экрана.

В предоставленном примере эта проблема наглядно демонстрируется. Четыре изображения (bik, pic и два увеличенных sprite) изначально загружаются в сцену. При клике в нижней части холста их координаты `xиy` изменяются на дробные значения 0.5 и 0.25 соответственно.

a.x += (aligned) ? 0.5 : -0.5;
a.y += (aligned) ? 0.25 : -0.25;

Без специальной обработки такие микро-сдвиги сразу же ухудшат визуальное качество пиксель-арта.

Решение: настройка конфигурации игры

Phaser предлагает два ключевых параметра в объекте конфигурации для борьбы с этой проблемой: pixelArt и roundPixels. Их необходимо задать при создании экземпляра Phaser.Game.

Параметр pixelArt: true включает фильтр CSS image-rendering: pixelated, который предотвращает сглаживание растровых изображений при их масштабировании. Это фундаментальная настройка для любого пиксель-арт проекта.

Параметр roundPixels: true заставляет рендерер движка округлять конечные координаты всех отрисовываемых объектов до целых чисел каждый кадр. Это решает проблему субпиксельного смещения.

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    roundPixels: true, // Округляет координаты объектов
    pixelArt: true,    // Включает резкое масштабирование пиксельной графики
    backgroundColor: '#000099',
    scene: Example
};
const game = new Phaser.Game(config);

В примере оба параметра активированы, что гарантирует чёткое отображение спрайтов даже при их движении на дробные пиксели.

Интерактивная демонстрация и зум камеры

Пример также включает интерактивные элементы для проверки работы настроек. В верхних углах экрана размещены два текстовых объекта, которые служат кнопками.

const left = this.add.text(10, 10, 'Left 0.5px', { font: '16px Courier' });
const right = this.add.text(680, 10, 'Right 0.5px', { font: '16px Courier' });

При клике на кнопку "Left 0.5px" увеличивается зум камеры (this.cameras.main.zoom), а при клике на "Right 0.5px" — уменьшается. Это позволяет приблизиться к объектам и детально рассмотреть, как ведут себя их пиксели при разных уровнях масштаба, даже с включённым roundPixels.

left.on('pointerdown', () => {
    this.cameras.main.zoom += 1;
});
right.on('pointerdown', () => {
    this.cameras.main.zoom -= 0.5;
});

Код, изменяющий scrollX камеры на 0.5 пикселя, закомментирован, но его можно активировать, чтобы увидеть, как roundPixels влияет и на рендеринг через камеру.

Логика переключения выравнивания объектов

Основная демонстрация происходит по клику в нижней части холста (pointer.y < 200). Обработчик события pointerdown инвертирует булеву переменную aligned и сдвигает все четыре изображения на фиксированные дробные значения.

this.input.on('pointerdown', (pointer) => {
    if (pointer.y < 200) { return; }
    aligned = !aligned;
    a.x += (aligned) ? 0.5 : -0.5;
    // ... аналогично для b, c, d
    a.y += (aligned) ? 0.25 : -0.25;
    // ...
});

Благодаря roundPixels: true, визуально эти сдвиги на 0.5 пикселя по X и 0.25 по Y не будут заметны — объекты останутся на целых пиксельных координатах. Если отключить эту настройку в конфиге, каждый клик будет приводить к лёгкому "дрожанию" и размытию спрайтов, особенно заметному при большом зуме.

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

Использование связки roundPixels: true и pixelArt: true в конфигурации Phaser — это простой и эффективный способ сохранить кристальную чёткость пиксельной графики. Эти настройки берут на себя заботу о выравнивании координат, позволяя разработчику сосредоточиться на геймдизайне и логике. Для экспериментов попробуйте: 1. Отключить roundPixels и понаблюдать за накоплением дробных координат и появлением артефактов. 2. Раскомментировать код, сдвигающий scrollX камеры, чтобы увидеть, как округление работает и для её позиции. 3. Поиграть с уровнем зума, чтобы понять, при каких масштабах эффект округления наиболее критичен.