О чем этот пример
При создании интерактивных спрайтов в Phaser по умолчанию используется их ограничивающий прямоугольник (bounding box) для проверки наведения курсора. Это может привести к ложным срабатываниям, особенно для объектов сложной формы, таких как космические корабли или круглые мячи. В этой статье мы разберем, как использовать геометрические фигуры Phaser для точной проверки попадания (hit test), чтобы взаимодействие было интуитивно понятным и соответствовало визуальной форме объекта. Этот подход критически важен для игр с плотным расположением элементов, головоломок или любых проектов, где точность ввода влияет на геймплей. Вы научитесь назначать спрайтам кастомные области взаимодействия в форме полигона, круга, эллипса и треугольника.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('ball', 'assets/sprites/wizball.png');
this.load.image('chick', 'assets/sprites/budbrain_chick.png');
this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
this.load.image('ship', 'assets/sprites/thrust_ship2.png');
this.load.image('car', 'assets/pics/supercars-parsec.png');
}
create ()
{
const sprite1 = this.add.sprite(200, 550, 'car').setOrigin(0);
sprite1.setInteractive(new Phaser.Geom.Polygon([ 0, 143, 0, 92, 110, 40, 244, 4, 330, 0, 458, 12, 574, 18, 600, 79, 594, 153, 332, 152, 107, 157 ]), Phaser.Geom.Polygon.Contains);
const sprite2 = this.add.sprite(150, 150, 'ball').setScale(2);
sprite2.setInteractive(new Phaser.Geom.Circle(45, 46, 45), Phaser.Geom.Circle.Contains);
const sprite3 = this.add.sprite(600, 200, 'chick').setScale(2);
sprite3.setInteractive(new Phaser.Geom.Ellipse(33, 65, 66, 133), Phaser.Geom.Ellipse.Contains);
const sprite4 = this.add.sprite(350, 300, 'eye');
sprite4.setInteractive(new Phaser.Geom.Rectangle(0, 0, 128, 128), Phaser.Geom.Rectangle.Contains);
const sprite5 = this.add.sprite(850, 350, 'ship').setScale(8);
sprite5.setInteractive(new Phaser.Geom.Triangle.BuildEquilateral(16, 0, 30), Phaser.Geom.Triangle.Contains);
// Input Event listeners
this.input.on('gameobjectover', (pointer, gameObject) =>
{
gameObject.setTint(0x7878ff);
});
this.input.on('gameobjectout', (pointer, gameObject) =>
{
gameObject.clearTint();
});
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Проблема стандартного подхода
По умолчанию, когда вы вызываете sprite.setInteractive() без параметров, Phaser делает спрайт кликабельным, используя его текстуру для создания прямоугольной области (хитбокса). Для прямоугольных спрайтов это работает отлично. Но представьте космический корабль с треугольным силуэтом или круглый мяч: курсор будет активировать спрайт, даже если находится в углу его ограничивающего прямоугольника, но вне видимой части самого объекта.
Это ухудшает пользовательский опыт. Решение — вручную задать геометрическую форму, которая точно соответствует визуальному контуру спрайта, используя второй аргумент setInteractive.
Создание кастомной области взаимодействия
Метод setInteractive может принимать два ключевых параметра: объект геометрической фигуры и функцию проверки принадлежности точки к этой фигуре (обычно Phaser.Geom.[Фигура].Contains).
Давайте создадим спрайт и назначим ему полигональную область. Координаты полигона задаются относительно начала спрайта (origin).
const sprite1 = this.add.sprite(200, 550, 'car').setOrigin(0);
sprite1.setInteractive(
new Phaser.Geom.Polygon([0, 143, 0, 92, 110, 40, 244, 4, 330, 0, 458, 12, 574, 18, 600, 79, 594, 153, 332, 152, 107, 157]),
Phaser.Geom.Polygon.Contains
);
Здесь мы сначала создаем спрайт машины и сбрасываем точку его отсчета (setOrigin(0)) в левый верхний угол. Это важно, потому что координаты вершин полигона будут рассчитываться от этой точки. Затем мы определяем интерактивную область в виде полигона, обводящего контур машины на изображении.
Разнообразие геометрических фигур
Phaser.Geom предоставляет несколько готовых фигур. Вот как задать область для круглого и овального спрайтов.
Для шара создадим круг. Первые два аргумента Circle — это координаты центра круга относительно спрайта, третий — радиус.
const sprite2 = this.add.sprite(150, 150, 'ball').setScale(2);
sprite2.setInteractive(
new Phaser.Geom.Circle(45, 46, 45),
Phaser.Geom.Circle.Contains
);
Для цыпленка, форма которого больше напоминает эллипс, используем соответствующую фигуру. Аргументы Ellipse: x, y центра, ширина и высота.
const sprite3 = this.add.sprite(600, 200, 'chick').setScale(2);
sprite3.setInteractive(
new Phaser.Geom.Ellipse(33, 65, 66, 133),
Phaser.Geom.Ellipse.Contains
);
Для прямоугольного спрайта (глаз) можно явно указать прямоугольник, что полезно, если нужна область меньше или больше текстуры.
const sprite4 = this.add.sprite(350, 300, 'eye');
sprite4.setInteractive(
new Phaser.Geom.Rectangle(0, 0, 128, 128),
Phaser.Geom.Rectangle.Contains
);
Фигуры из примитивов: Треугольник
Для простых форм, таких как стрелка или треугольный корпус корабля, идеально подойдет треугольник. В примере используется равносторонний треугольник, построенный от верхней центральной точки.
const sprite5 = this.add.sprite(850, 350, 'ship').setScale(8);
sprite5.setInteractive(
Phaser.Geom.Triangle.BuildEquilateral(16, 0, 30),
Phaser.Geom.Triangle.Contains
);
Статический метод BuildEquilateral создает равносторонний треугольник. Его параметры: x, y первой вершины (верхней) и длина стороны. Эта форма точно описывает носовую часть корабля.
Визуальная обратная связь для наведения
Чтобы продемонстрировать точность новых хитовых областей, добавим обработчики событий наведения и увода курсора. Они сработают только тогда, когда курсор находится внутри заданной нами геометрической формы.
this.input.on('gameobjectover', (pointer, gameObject) => {
gameObject.setTint(0x7878ff); // Подсвечиваем спрайт синим
});
this.input.on('gameobjectout', (pointer, gameObject) => {
gameObject.clearTint(); // Убираем подсветку
});
Событие gameobjectover будет генерироваться системой ввода Phaser только при пересечении курсора с кастомной геометрической областью спрайта, а не с его ограничивающим прямоугольником. Это дает нам точную и корректную обратную связь.
Что попробовать дальше
Использование геометрических фигур для проверки наведения — мощный инструмент для повышения качества взаимодействия в вашей игре. Он устраняет раздражающие ложные срабатывания и делает интерфейс более отзывчивым. **Идеи для экспериментов:** 1. Попробуйте создать сложный полигон для спрайта неправильной формы, используя инструменты для получения координат точек. 2. Комбинируйте несколько простых фигур (например, два круга и прямоугольник) для составной хитовой области, используя пользовательскую функцию проверки. 3. Динамически меняйте геометрическую область в зависимости от состояния анимации спрайта (например, удар мечом).
