О чем этот пример
При создании пиксель-арт игр на Phaser разработчики часто сталкиваются с неприятным эффектом: спрайты и тайлы начинают "дрожать" при движении камеры или при нецелых координатах объектов. Это происходит из-за субпиксельного рендеринга, когда изображение пытается встать между физическими пикселями экрана. К счастью, Phaser предоставляет простое и эффективное решение — метод `camera.setRoundPixels()`. Эта статья на практическом примере покажет, как за один вызов API избавиться от размытия и сделать вашу пиксель-арт графику чёткой и стабильной.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('grid', 'assets/sprites/128x128-v2.png');
}
create ()
{
this.add.image(400.8, 300.3, 'grid').setScale(1.9);
this.cameras.main.setRoundPixels(true);
this.cameras.main.setZoom(1.3);
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
pixelArt: true,
scene: Example
}
const game = new Phaser.Game(config);
Проблема: субпиксельное смещение и размытие
В исходном коде примера создаётся спрайт с текстурой 128x128 пикселей. Однако его начальные координаты заданы не целыми числами, а значениями с плавающей точкой: (400.8, 300.3). Кроме того, спрайт масштабируется (setScale(1.9)), а камера увеличена (setZoom(1.3)).
В обычном режиме, чтобы отрисовать объект в такой позиции, движку приходится интерполировать цвета соседних пикселей, что приводит к визуальному "размытию" или "двоению" контуров. При движении камеры этот эффект проявляется как заметное дрожание.
this.add.image(400.8, 300.3, 'grid').setScale(1.9);
Решение: принудительное округление координат
Метод setRoundPixels() камеры решает эту проблему. Когда ему передаётся значение true, все координаты отрисовываемых объектов (включая спрайты, тайлы, графику) нацело округляются до ближайшего целого пикселя **перед** финальным рендерингом на экран.
Это происходит на уровне конкретной камеры, что позволяет применять настройку выборочно. В примере метод вызывается для основной камеры this.cameras.main.
this.cameras.main.setRoundPixels(true);
После этого, несмотря на исходные координаты (400.8, 300.3), спрайт будет отрисован в позиции (401, 300). Дрожание исчезнет, границы пикселей станут чёткими.
Важное взаимодействие: pixelArt и зум
Для корректной работы пиксель-арт графики в Phaser критически важна настройка pixelArt: true в конфигурации игры. Она отключает линейную интерполяцию текстур при масштабировании, сохраняя чёткие пиксельные границы.
const config = {
// ... другие настройки
pixelArt: true
}
Метод setRoundPixels() идеально дополняет эту настройку. Особенно его эффект заметен при использовании зума камеры (setZoom()), как в примере. Без округления пикселей увеличенное изображение могло бы выглядеть сильно замыленным.
this.cameras.main.setZoom(1.3);
Когда использовать и практические советы
Используйте setRoundPixels(true) всегда, когда работаете с пиксель-арт стилистикой, тайловыми картами или любыми спрайтами, где важна чёткость пиксельных границ.
**Важные моменты:** 1. Округление применяется к конечной позиции на экране с учётом всех преобразований (позиция, масштаб, вращение, зум камеры). 2. Метод влияет только на визуальное отображение, физические тела (если они есть) продолжают использовать точные координаты с плавающей точкой для расчётов. 3. Если в игре несколько камер (например, для разделённого экрана или UI-слоя), настройку нужно применить к каждой, где требуется чёткость.
// Включение для UI-камеры
this.cameras.getCamera('uiCamera').setRoundPixels(true);
Что попробовать дальше
Метод camera.setRoundPixels() — это мощный, но простой инструмент для контроля качества рендеринга в пиксель-арт проектах на Phaser. Его использование практически обязательно для достижения стабильной и чёткой картинки.
**Идеи для экспериментов:**
1. Создайте сцену с плавно движущейся камерой (camera.scrollX) и понаблюдайте за дрожанием спрайтов с выключенным и включённым setRoundPixels.
2. Попробуйте применить метод только к камере, отвечающей за игровой мир, оставив UI-камеру без него, чтобы увидеть разницу.
3. Поэкспериментируйте с различными значениями зума камеры и масштаба спрайтов, чтобы понять, при каких условиях эффект размытия проявляется сильнее всего.
