О чем этот пример
При разработке игр часто возникает задача динамического масштабирования элементов интерфейса под разные области экрана. Вместо ручных расчетов можно использовать встроенные геометрические методы Phaser. В этой статье разберем, как работает `Phaser.Geom.Rectangle.FitInside()` — метод, который автоматически подгоняет один прямоугольник внутрь другого с сохранением пропорций. Это особенно полезно для создания адаптивных окон меню, карт мини-игр или ограничения зоны движения камеры. Вы научитесь применять этот подход для динамической компоновки UI без сложной математики.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create ()
{
const graphics = this.add.graphics({ fillStyle: { color: 0x0000aa }, lineStyle: { color: 0xaa0000 } });
const rectangles = [];
for (let x = 0; x < 4; x++)
{
rectangles[x] = [];
for (let y = 0; y < 3; y++)
{
const width = Phaser.Math.Between(100, 200);
const height = Phaser.Math.Between(100, 200);
rectangles[x][y] = new Phaser.Geom.Rectangle(x * 200, y * 200, width, height);
}
}
const rect = new Phaser.Geom.Rectangle(0, 0, 150, 100);
this.input.on('pointermove', pointer =>
{
const x = Math.floor(pointer.x / 200);
const y = Math.floor(pointer.y / 200);
Phaser.Geom.Rectangle.FitInside(rect, rectangles[x][y]);
redraw();
});
redraw();
function redraw ()
{
graphics.clear();
for (let x = 0; x < 4; x++)
{
for (let y = 0; y < 3; y++)
{
graphics.strokeRectShape(rectangles[x][y]);
}
}
graphics.fillRectShape(rect);
}
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сетки прямоугольников
В примере создается сетка из 12 прямоугольников (4x3), каждый со случайными размерами. Это имитирует различные целевые области на экране, куда мы будем вписывать наш основной прямоугольник.
const rectangles = [];
for (let x = 0; x < 4; x++)
{
rectangles[x] = [];
for (let y = 0; y < 3; y++)
{
const width = Phaser.Math.Between(100, 200);
const height = Phaser.Math.Between(100, 200);
rectangles[x][y] = new Phaser.Geom.Rectangle(x * 200, y * 200, width, height);
}
}
Ключевые моменты:
- Phaser.Math.Between генерирует случайную ширину и высоту для каждого прямоугольника.
- Позиция каждого прямоугольника рассчитывается с шагом 200 пикселей по осям X и Y, создавая равномерную сетку.
- Все прямоугольники хранятся в двумерном массиве для удобного доступа по координатам сетки.
Создание основного прямоугольника и обработка ввода
Создается прямоугольник-контейнер фиксированного размера (150x100), который будет масштабироваться. Его позиция изначально в точке (0, 0).
const rect = new Phaser.Geom.Rectangle(0, 0, 150, 100);
Для интерактивности добавляется обработчик движения указателя. При движении мыши вычисляется, в какую ячейку сетки попадает курсор, и прямоугольник rect подгоняется под соответствующий прямоугольник сетки.
this.input.on('pointermove', pointer =>
{
const x = Math.floor(pointer.x / 200);
const y = Math.floor(pointer.y / 200);
Phaser.Geom.Rectangle.FitInside(rect, rectangles[x][y]);
redraw();
});
Как это работает:
- Math.floor(pointer.x / 200) определяет индекс столбца сетки (0-3) на основе координаты X курсора.
- Math.floor(pointer.y / 200) определяет индекс строки сетки (0-2).
- Phaser.Geom.Rectangle.FitInside(rect, rectangles[x][y]) — вызов, который модифицирует прямоугольник rect, чтобы он поместился внутри целевого прямоугольника сетки, сохраняя свои исходные пропорции.
Как работает метод FitInside
Метод Phaser.Geom.Rectangle.FitInside(source, target) изменяет параметры первого переданного прямоугольника (source), чтобы он полностью поместился внутри второго (target), не искажая свои пропорции. Это похоже на режим object-fit: contain в CSS.
Алгоритм метода: 1. Вычисляется соотношение сторон исходного прямоугольника (ширина / высота). 2. Проверяется, можно ли вписать прямоугольник по ширине. Если ширина исходного прямоугольника, масштабированная по высоте цели, превышает целевую ширину, то масштабирование происходит по ширине. 3. Позиция прямоугольника центрируется внутри целевой области.
Важно: метод изменяет сам объект source (его координаты x, y, width, height), а не возвращает новый объект.
Визуализация с помощью Graphics
Для отрисовки всех прямоугольников используется объект Graphics. Функция redraw очищает холст и заново рисует сетку и основной прямоугольник.
function redraw ()
{
graphics.clear();
for (let x = 0; x < 4; x++)
{
for (let y = 0; y < 3; y++)
{
graphics.strokeRectShape(rectangles[x][y]);
}
}
graphics.fillRectShape(rect);
}
Детали:
- graphics.clear() — обязательный вызов перед каждой перерисовкой, чтобы удалить предыдущие кадры.
- graphics.strokeRectShape(rectangles[x][y]) — рисует контур каждого прямоугольника сетки красным цветом (как задано в lineStyle).
- graphics.fillRectShape(rect) — заливает синим цветом (как задано в fillStyle) наш масштабируемый прямоугольник, показывая результат работы FitInside.
Цвета задаются при создании Graphics объекта: { fillStyle: { color: 0x0000aa }, lineStyle: { color: 0xaa0000 } }.
Что попробовать дальше
Метод Phaser.Geom.Rectangle.FitInside — это мощный инструмент для управления геометрией в играх. Он избавляет от необходимости писать собственные алгоритмы масштабирования с сохранением пропорций.
Идеи для экспериментов:
- Используйте FitInside для динамического масштабирования спрайтов интерфейса под разные разрешения экрана.
- Попробуйте комбинировать с Phaser.Geom.Rectangle.CenterOn для более точного позиционирования.
- Создайте систему всплывающих окон, которые автоматически подстраиваются под безопасную зону экрана, рассчитанную для разных устройств.
