О чем этот пример
При работе с геометрией в играх часто возникают ситуации, когда координаты объектов вычисляются с плавающей точкой, но для корректного отображения или физики требуются целочисленные значения. Накопление дробных частей может привести к визуальным артефактам или ошибкам коллизий. В этой статье мы разберем, как использовать метод `Phaser.Geom.Rectangle.CeilAll()` для автоматического округления координат и размеров прямоугольника. Это особенно полезно для пиксель-арт игр, сеточного позиционирования или привязки объектов к целочисленным координатам после анимаций и трансформаций.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
size = 50;
y = 0;
graphics;
rect2;
rect1;
create ()
{
this.graphics = this.add.graphics({ fillStyle: { color: 0x0000aa } });
this.rect1 = new Phaser.Geom.Rectangle(100, 0, 50, 50);
this.rect2 = new Phaser.Geom.Rectangle(450, 0, 50, 50);
}
update ()
{
this.y += 0.05;
this.size += 0.05;
this.rect1.y = this.rect2.y = this.y;
this.rect1.setSize(this.size);
this.rect2.setSize(this.size);
Phaser.Geom.Rectangle.CeilAll(this.rect2);
this.graphics.clear();
this.graphics.fillRectShape(this.rect1);
this.graphics.fillRectShape(this.rect2);
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что делает CeilAll?
Метод Phaser.Geom.Rectangle.CeilAll() применяет математическую функцию округления вверх (Math.ceil) ко всем основным свойствам прямоугольника: координатам `x,y, ширинеwidthи высотеheight`. Это означает, что каждое значение будет увеличено до ближайшего целого числа, которое больше или равно исходному.
// Исходный прямоугольник с дробными значениями
let rect = new Phaser.Geom.Rectangle(10.2, 15.7, 45.1, 30.8);
// После вызова CeilAll
Phaser.Geom.Rectangle.CeilAll(rect);
// Теперь rect имеет значения: x=11, y=16, width=46, height=31
В исходном примере этот метод вызывается каждый кадр для rect2, в то время как rect1 остается с исходными дробными значениями. Это позволяет наглядно сравнить визуальный результат округления.
Разбор сцены и инициализации
В классе сцены Example определены свойства для управления размером, позицией и графикой. В методе create() инициализируются два одинаковых прямоугольника и объект Graphics для их отрисовки.
create ()
{
this.graphics = this.add.graphics({ fillStyle: { color: 0x0000aa } });
this.rect1 = new Phaser.Geom.Rectangle(100, 0, 50, 50);
this.rect2 = new Phaser.Geom.Rectangle(450, 0, 50, 50);
}
Обрати внимание, что оба прямоугольника (rect1 и rect2) изначально создаются с целыми координатами и размерами. Различия между ними проявятся позже, в игровом цикле.
Игровой цикл и анимация
В методе update(), который выполняется каждый кадр, происходит увеличение переменных `yиsize`. Эти значения с плавающей точкой присваиваются свойствам обоих прямоугольников.
update ()
{
this.y += 0.05;
this.size += 0.05;
this.rect1.y = this.rect2.y = this.y;
this.rect1.setSize(this.size);
this.rect2.setSize(this.size);
}
Таким образом, на каждом кадре rect1 и rect2 получают одинаковые дробные значения для позиции по оси Y и для размера. Без дополнительной обработки оба прямоугольника визуально выглядели бы идентично.
Ключевое применение CeilAll
После обновления свойств для rect2 вызывается метод CeilAll(). Это модифицирует его геометрию, в то время как rect1 остается с исходными дробными значениями.
Phaser.Geom.Rectangle.CeilAll(this.rect2);
Затем холст для рисования очищается, и оба прямоугольника отрисовываются заново с помощью fillRectShape().
this.graphics.clear();
this.graphics.fillRectShape(this.rect1);
this.graphics.fillRectShape(this.rect2);
Визуально прямоугольник rect2 будет двигаться и увеличиваться рывками, так как его координаты и размеры "привязываются" к целым числам, округленным вверх. Прямоугольник rect1 будет двигаться и расти плавно.
Когда это полезно на практике?
1. **Пиксель-арт и целочисленное позиционирование:** Гарантирует, что спрайты не будут размыты из-за субпиксельного рендеринга, сохраняя четкость пиксельной графики.
2. **Сетки и тайловые карты:** При расчете позиции объекта в ячейке сетки часто удобнее работать с целыми индексами.
3. **Оптимизация коллизий:** Некоторые алгоритмы проверки пересечений могут работать быстрее или надежнее с целочисленными значениями, особенно при использовании битовых операций или индексировании в массивы.
4. **Согласованность состояния:** Если логика игры (например, путь ИИ или сохранение) опирается на целые координаты, CeilAll помогает привести визуальное представление в соответствие с этой логикой после плавных движений.
Что попробовать дальше
Метод Phaser.Geom.Rectangle.CeilAll — это простой, но мощный инструмент для управления точностью геометрии. Он помогает избежать рассинхронизации между плавной визуальной анимацией и игровой логикой, требующей целочисленных значений.
**Идеи для экспериментов:**
* Сравните поведение с другими методами округления, например, FloorAll или RoundAll.
* Примените CeilAll не ко всем, а только к определенным свойствам прямоугольника (например, только к `xиy`), комбинируя с ручным округлением.
* Используйте этот метод для "привязки" курсора мыши или зоны взаимодействия к сетке в редакторе уровней.
