О чем этот пример
При разработке игр для разных устройств управление тем, как игра отображается на экранах с разными соотношениями сторон, становится критически важным. Phaser предоставляет мощную систему масштабирования (`Phaser.Scale`), но некоторые ее тонкости могут привести к неожиданным результатам. Этот пример демонстрирует работу режима `EXPAND` в связке с настройками `max`, что позволяет контролировать максимальное расширение игрового холста, предотвращая его выход за заданные рамки. Понимание этого механизма поможет вам создавать игры, которые корректно и предсказуемо выглядят на любых экранах, от мобильных до десктопных.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
const width = 1080;
const height = 1920;
class Example extends Phaser.Scene
{
create ()
{
const graphics = this.add.graphics();
// draw a red rectangle with outline only the size of the game
graphics.lineStyle(20, 0xff0000);
graphics.strokeRect(0, 0, width, height);
// draw a blue circle at all four corners of the game
graphics.fillStyle(0x0000ff);
graphics.fillCircle(0, 0, 50);
graphics.fillCircle(width, 0, 50);
graphics.fillCircle(0, height, 50);
graphics.fillCircle(width, height, 50);
}
}
const config = {
type: Phaser.AUTO,
backgroundColor: '#2dab2d',
scale: {
parent: 'phaser-example',
mode: Phaser.Scale.EXPAND,
// mode: Phaser.Scale.FIT,
// mode: Phaser.Scale.RESIZE,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: width,
height: height,
max: {
width: 4000,
height: 100,
}
},
scene: Example
};
const game = new Phaser.Game(config);
Проблема: неограниченное расширение
По умолчанию режим Phaser.Scale.EXPAND растягивает игровое поле, чтобы оно заполнило весь доступный контейнер (например, окно браузера), сохраняя при этом исходное соотношение сторон игры. Это полезно для максимального использования экранного пространства. Однако, без дополнительных ограничений, игра может растянуться до огромных размеров, если контейнер очень широкий или очень высокий, что часто ломает визуальный дизайн.
Рассмотрим сценарий игры с портретной ориентацией (1080x1920), которую запускают на ультрашироком мониторе. Без ограничений режим EXPAND будет растягивать холст по ширине, сильно искажая или оставляя пустые области по вертикали.
Решение: настройка объекта `max`
Конфигурация масштабирования в Phaser позволяет задать объект max, который определяет максимальные ширину и высоту, до которых может быть растянут игровой холст. Это ключевой инструмент для контроля.
В нашем примере объект max установлен так:
max: {
width: 4000,
height: 100,
}
Это означает: игра может расширяться по ширине максимум до 4000 пикселей, а по высоте — только до 100 пикселей. Важно понимать, что max ограничивает конечный размер *холста*, а не игрового мира. Игровой мир (логические координаты) остается размером 1080x1920, но визуально он будет отмасштабирован, чтобы поместиться в эти ограничения, сохраняя соотношение сторон.
Анализ кода и визуализации
Код сцены рисует контур игрового поля и маркеры в углах, чтобы наглядно показать границы.
create ()
{
const graphics = this.add.graphics();
// Рисуем красный прямоугольник по контуру игрового поля
graphics.lineStyle(20, 0xff0000);
graphics.strokeRect(0, 0, width, height);
// Рисуем синие круги во всех четырех углах игрового поля
graphics.fillStyle(0x0000ff);
graphics.fillCircle(0, 0, 50);
graphics.fillCircle(width, 0, 50);
graphics.fillCircle(0, height, 50);
graphics.fillCircle(width, height, 50);
}
Фигуры рисуются относительно логических координат игрового мира (от 0,0 до 1080,1920). Когда срабатывает режим EXPAND с ограничением max.height: 100, физическая высота холста не может превысить 100 пикселей. Система масштабирования вычисляет коэффициент, при котором логическая высота в 1920 пикселей умещается в 100 физических, и применяет тот же коэффициент к ширине. В результате холст будет очень узким и высоким (в рамках ограничения по высоте), а красная рамка и синие круги будут сильно сжаты по горизонтали. Объект max действует как жесткий "потолок" для физических размеров.
Сравнение с другими режимами
Давайте кратко сравним поведение EXPAND с другими популярными режимами, чтобы прояснить его уникальность:
* Phaser.Scale.FIT: Гарантирует, что вся игровая область будет видна, без обрезки, добавляя черные полосы (или фоновый цвет) при несовпадении соотношений сторон. Ограничения max и min также работают.
* Phaser.Scale.RESIZE: Игровой холст просто принимает размер своего контейнера. Размер игрового мира в логических координатах меняется вместе с размером холста (это важно!). Здесь width и height из конфига игнорируются в пользу реальных размеров контейнера.
// mode: Phaser.Scale.FIT,
// mode: Phaser.Scale.RESIZE,
Режим EXPAND — это компромисс: он старается заполнить весь контейнер как RESIZE, но не изменяет логические координаты мира, как FIT. Ограничения max/min дают контроль над этим заполнением.
Практические рекомендации
Как эффективно использовать max и min?
1. **Защита от крайностей:** Всегда устанавливайте разумные max значения, особенно если ваша игра рассчитана на определенную ориентацию. Для портретной игры ограничьте max.width, для ландшафтной — max.height.
2. **Соотношение с min:** Объект min работает симметрично, предотвращая слишком сильное сжатие холста. Их часто используют вместе для определения "коридора" допустимых размеров.
3. **Тестирование:** Проверяйте игру в окне браузера, динамически меняя его размер. Используйте инструменты разработчика для эмуляции разных устройств.
4. **Фон и UI:** Помните, что backgroundColor заполнит весь контейнер, даже если сам холст (с игрой) меньше из-за ограничений max. Интерфейс, построенный на this.add или графике, будет масштабироваться вместе с игровым миром. Для элементов, которые должны оставаться привязанными к краям экрана (контейнера), используйте this.scale события и пересчитывайте позиции вручную или используйте камеры.
Что попробовать дальше
Режим масштабирования EXPAND в связке с объектами max и min — это мощный инструмент для адаптивного дизайна игры, который позволяет гибко заполнять доступное пространство, но в установленных вами рамках. Он предсказуемо масштабирует визуальное отображение, не затрагивая логику координат игрового мира.
**Идеи для экспериментов:**
1. Поменяйте значения max.width и max.height местами и понаблюдайте, как меняется поведение при растягивании окна по горизонтали и вертикали.
2. Добавьте объект min с небольшими значениями и посмотрите, как игра ведет себя при сильном уменьшении окна.
3. Смените режим на FIT или RESIZE с теми же ограничениями max и проанализируйте разницу в отображении красной рамки и синих кругов.
4. Создайте простой спрайт и попробуйте позиционировать его относительно this.scale.gameSize (размер физического холста) и this.sys.game.config (логический размер мира).
