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

Разработка мобильных игр часто сопровождается проблемами отображения на разных устройствах. Один из коварных багов — это неожиданное поведение автоцентровки (`autoCenter`) в iOS, когда игровая сцена может смещаться или масштабироваться не так, как задумано. Эта статья поможет вам понять, как отлаживать подобные проблемы, используя визуальные дебаг-инструменты прямо в Phaser. Вы научитесь быстро выявлять и исправлять несоответствия в позиционировании, что особенно критично для пиксель-арт игр, где каждый пиксель на счету.

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

Живой запуск

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

Исходный код


var gameContainerSizeDebug;

class MainScene extends Phaser.Scene {

    constructor() {
        super({ key: "MainScene" });
    }

    create() {
        
        gameContainerSizeDebug = this.add.graphics({ 
            lineStyle: { width: 1, color: 0xffffff, alpha: 1 }, 
            fillStyle: { color: 0xff0000, alpha: 0 } 
        }).strokeRect(0, 0, 144, 256).strokeCircle(144 / 2, 256 / 2, 42).beginPath().moveTo(0, 0).lineTo(144, 256).strokePath().beginPath().moveTo(144, 0).lineTo(0, 256).strokePath().beginPath().moveTo(-1024, 256/2).lineTo(1024,256/2).strokePath().beginPath().moveTo(144/2, -1024).lineTo(144/2,1024).strokePath();

    }
}

const config = {
    type: Phaser.AUTO,
    width: 144,
    height: 256,
    backgroundColor: '#333',
    pixelArt: true,
    scale: {
        mode: Phaser.Scale.FIT,
        autoCenter: Phaser.Scale.CENTER_BOTH,
        zoom: 1,
    },
    scene: [ MainScene ]
};

const game = new Phaser.Game(config)

// http://bs-local.com:8080/mobile.html?src=src\bugs\6862%20iOS%20autocenter%20inconsistent.js
// https://phaser.io/sandbox/A3KSEgq7

Проблема: автоцентровка работает неодинаково

При использовании Phaser.Scale.FIT и Phaser.Scale.CENTER_BOTH движок должен автоматически центрировать и подгонять размер игрового холста под доступное пространство на устройстве. Однако на iOS могут возникать расхождения: например, сцена центрируется не по пиксельной сетке, что приводит к размытости в пиксель-арте, или смещается относительно физических границ экрана.

Этот баг сложно уловить "на глаз", особенно если интерфейс игры не имеет четких ориентиров. Нужен надежный способ визуализации реальных границ и центра игрового мира.

Создаем дебаг-слой для визуализации

В примере используется объект Graphics для отрисовки примитивов прямо поверх игрового мира. Это позволяет в реальном времени увидеть, где находятся ключевые точки.

Создаем графический объект в методе create() сцены. Мы задаем ему стиль линии и заливки. Обратите внимание, что заливка полностью прозрачна (alpha: 0), так как нам нужны только контуры.

gameContainerSizeDebug = this.add.graphics({ 
    lineStyle: { width: 1, color: 0xffffff, alpha: 1 }, 
    fillStyle: { color: 0xff0000, alpha: 0 } 
});

Рисуем ориентиры: прямоугольник, круг и оси

После создания объекта мы вызываем цепочку методов для отрисовки фигур. Каждая фигура служит определенной цели для отладки.

1. **Прямоугольник (strokeRect)**: Обозначает номинальные границы игрового мира (144x256 пикселей). Если он не совпадает с видимой областью на устройстве — проблема в масштабировании. 2. **Круг (strokeCircle)**: Показывает центр игрового мира. Его смещение от визуального центра экрана устройства укажет на проблему с autoCenter. 3. **Диагонали и оси**: Длинные линии, выходящие за границы прямоугольника, помогают оценить смещение и понять, где находится "ноль" координат (верхний левый угол мира).

.strokeRect(0, 0, 144, 256)
.strokeCircle(144 / 2, 256 / 2, 42)
.beginPath().moveTo(0, 0).lineTo(144, 256).strokePath()
.beginPath().moveTo(144, 0).lineTo(0, 256).strokePath()
.beginPath().moveTo(-1024, 256/2).lineTo(1024,256/2).strokePath()
.beginPath().moveTo(144/2, -1024).lineTo(144/2,1024).strokePath();

Настройка Scale Manager — ключ к пониманию

Поведение центрирования и масштабирования полностью определяется конфигурацией scale в объекте конфига игры.

* mode: Phaser.Scale.FIT гарантирует, что игра впишется в доступную область, сохраняя пропорции. Могут появиться черные поля (letterbox). * autoCenter: Phaser.Scale.CENTER_BOTH центрирует игровую область по горизонтали и вертикали внутри этих полей. * zoom: 1 устанавливает базовый уровень масштаба. Изменение этого значения может использоваться для принудительного пиксельного соответствия на HiDPI-экранах.

scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    zoom: 1,
}

Как интерпретировать результат на устройстве

Запустите игру на целевом iOS-устройстве или в симуляторе.

* **Идеальный случай**: Белый прямоугольник и круг находятся ровно по центру экрана устройства, линии осей делят экран пополам. Черные поля (если есть) симметричны. * **Признак проблемы**: Прямоугольник смещен в сторону, круг не в центре экрана, оси проходят не через визуальный центр. Это прямое указание на баг в реализации autoCenter для данной версии iOS/браузера.

Полученную "картинку" можно сфотографировать или сделать скриншот — это будет наглядное доказательство проблемы для issue-трекера или для поиска workaround.

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

Использование графического дебаг-слоя — это простой и мощный способ диагностики проблем с отображением на разных платформах. С его помощью вы можете не только подтвердить баг, но и начать искать обходные пути: например, вручную корректировать позицию сцены после определения реальных границ через this.scale.gameSize или this.cameras.main. Для экспериментов попробуйте изменить mode на Phaser.Scale.NONE и самостоятельно реализовать логику позиционирования, или используйте zoom для принудительного целочисленного масштабирования, чтобы избежать размытия пиксель-арта.