О чем этот пример
Определение, находится ли курсор мыши (или любая другая точка) над спрайтом — одна из базовых задач во многих типах игр. В Phaser 3 для этого есть удобный метод `transform.hasPoint()`. В этой статье мы разберем, как работает этот метод на практическом примере, где проверка выполняется для цепочки связанных объектов и для двух разных камер. Освоив этот подход, вы сможете легко реализовывать логику наведения, кликов по сложным составным объектам и корректно обрабатывать взаимодействие в играх с несколькими областями просмотра (камерами).
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: {
preload: preload,
create: create,
update: update
},
width: 800,
height: 600
};
var game = new Phaser.Game(config);
var image0;
var image1;
var image2;
var image3;
var image4;
var camera0;
var mainCamera;
var mouse = { x: 0, y: 0 };
function preload() {
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('atari', 'assets/sprites/atari130xe.png');
}
function create() {
image0 = this.add.image(0, 0, 'atari');
image1 = this.add.image(100, 108, 'atari');
image2 = this.add.image(100, 108, 'atari');
image3 = this.add.image(100, 108, 'atari');
image4 = this.add.image(100, 108, 'atari');
image0.transform.add(image1.transform);
image1.transform.add(image2.transform);
image2.transform.add(image3.transform);
image3.transform.add(image4.transform);
document.getElementsByTagName('canvas')[0].onmousemove = function (e) {
mouse.x = e.clientX;
mouse.y = e.clientY;
};
mainCamera = this.cameras.main;
camera0 = this.cameras.add(0, 300, 200, 200);
mainCamera.x = 200;
}
function update()
{
// main camera
if (image0.transform.hasPoint(mouse.x, mouse.y, mainCamera))
{
console.log('point over image0 on mainCamera');
}
if (image1.transform.hasPoint(mouse.x, mouse.y, mainCamera))
{
console.log('point over image1 on mainCamera');
}
if (image2.transform.hasPoint(mouse.x, mouse.y, mainCamera))
{
console.log('point over image2 on mainCamera');
}
if (image3.transform.hasPoint(mouse.x, mouse.y, mainCamera))
{
console.log('point over image3 on mainCamera');
}
if (image4.transform.hasPoint(mouse.x, mouse.y, mainCamera))
{
console.log('point over image4 on mainCamera');
}
// second camera
if (image0.transform.hasPoint(mouse.x, mouse.y, camera0))
{
console.log('point over image0 on camera0');
}
if (image1.transform.hasPoint(mouse.x, mouse.y, camera0))
{
console.log('point over image1 on camera0');
}
if (image2.transform.hasPoint(mouse.x, mouse.y, camera0))
{
console.log('point over image2 on camera0');
}
if (image3.transform.hasPoint(mouse.x, mouse.y, camera0))
{
console.log('point over image3 on camera0');
}
if (image4.transform.hasPoint(mouse.x, mouse.y, camera0))
{
console.log('point over image4 on camera0');
}
}
Суть примера: иерархия объектов и несколько камер
В данном примере создается пять одинаковых спрайтов, связанных в иерархическую цепочку с помощью метода transform.add(). Это означает, что преобразования (позиция, масштаб, поворот) image1 будут относительно image0, image2 — относительно image1 и так далее.
Также в сцене работают две камеры: основная (mainCamera) и дополнительная (camera0), которая отображает уменьшенную область. В функции update() для каждой картинки и для каждой камеры проверяется, попадает ли текущее положение мыши в границы этого спрайта с учетом его преобразований и конкретной камеры. Результат выводится в консоль.
image0.transform.add(image1.transform);
image1.transform.add(image2.transform);
// ...
if (image0.transform.hasPoint(mouse.x, mouse.y, mainCamera)) {
console.log('point over image0 on mainCamera');
}
Как работает transform.hasPoint()
Метод hasPoint(x, y, camera) объекта transform (который есть у любого игрового объекта, например, Image) возвращает true или false. Он проверяет, находится ли переданная точка с координатами (x, y) **в мировых координатах** внутри прямоугольной области (bounding box) этого объекта.
Ключевой нюанс — метод учитывает все текущие преобразования объекта: его позицию, масштаб, поворот, а также преобразования всех его родителей в иерархии. Третий аргумент — camera — определяет, через какую камеру мы «смотрим» на объект. Это критически важно, потому что камера может смещать, масштабировать или обрезать видимую область.
// Проверка для image2 через основную камеру
let isOverImage2 = image2.transform.hasPoint(mouseX, mouseY, mainCamera);
// Проверка для того же image2, но через камеру camera0
let isOverImage2InSmallCam = image2.transform.hasPoint(mouseX, mouseY, camera0);
В примере координаты мыши (mouse.x, mouse.y) — это координаты относительно окна браузера. Phaser внутри метода корректно преобразует их для сравнения с объектом в контексте указанной камеры.
Создание иерархии объектов
Чтобы продемонстрировать, что hasPoint учитывает иерархию, спрайты связываются друг с другом. image0 служит корневым родителем для image1, image1 — для image2 и т.д.
image0 = this.add.image(0, 0, 'atari');
image1 = this.add.image(100, 108, 'atari');
// ...
image0.transform.add(image1.transform);
После такой привязки позиция image1 становится относительной к image0. Если переместить image0, все его дочерние элементы (image1-image4) также сместятся. Метод hasPoint для дочернего объекта автоматически учитывает преобразования всех его предков в цепочке, что делает проверку корректной для сложных составных объектов.
Работа с несколькими камерами
В примере создается вторая камера camera0. Она отображает только область с координатами (0, 300) размером 200x200 пикселя.
mainCamera = this.cameras.main;
camera0 = this.cameras.add(0, 300, 200, 200);
mainCamera.x = 200; // Сдвигаем основную камеру вправо
Одна и та же точка в координатах окна браузера может находиться над объектом в одной камере, но не находиться в другой, если эта область не видна во второй камере или объект там отображается иначе. Поэтому в update проверка выполняется дважды: для mainCamera и для camera0. Это позволяет реализовывать интерфейсы с мини-картами или несколькими независимыми областями просмотра.
// Проверка для image0 в двух камерах
if (image0.transform.hasPoint(mouse.x, mouse.y, mainCamera)) { /* ... */ }
if (image0.transform.hasPoint(mouse.x, mouse.y, camera0)) { /* ... */ }
Собираем всё вместе в update()
Вся логика проверки сосредоточена в функции update(), которая выполняется на каждом кадре. Координаты мыши обновляются через слушатель событий DOM.
document.getElementsByTagName('canvas')[0].onmousemove = function (e) {
mouse.x = e.clientX;
mouse.y = e.clientY;
};
Затем для каждого спрайта (image0...image4) и для каждой камеры (mainCamera, camera0) вызывается hasPoint. Если проверка возвращает true, в консоль выводится соответствующее сообщение, позволяя в реальном времени наблюдать, над каким объектом и в каком «видепорте» находится курсор.
if (image2.transform.hasPoint(mouse.x, mouse.y, mainCamera))
{
console.log('point over image2 on mainCamera');
}
Такой подход обеспечивает точное и эффективное определение взаимодействия даже в сложных сценах.
Что попробовать дальше
Метод transform.hasPoint() — это мощный и точный инструмент Phaser для проверки попадания точки в объект с учетом всех преобразований и конкретной камеры. Он идеально подходит для реализации наведения, выбора объектов или нестандартных областей клика в иерархических структурах.
**Идеи для экспериментов:**
1. Добавьте объектам вращение или масштаб и убедитесь, что hasPoint продолжает работать корректно.
2. Создайте составной объект (например, группу частей корабля) и проверяйте попадание в него целиком.
3. Реализуйте логику «клика» по объекту только в одной из нескольких камер, имитируя взаимодействие с элементами мини-карты.
