О чем этот пример
При разработке игр часто возникает необходимость работать с разными системами координат. Игровые объекты живут в мировых координатах, а пользователь взаимодействует с экраном через координаты указателя. Phaser Camera предоставляет удобный метод `getWorldPoint` для преобразования координат с экрана в мировую систему. Эта техника незаменима для создания кликабельных объектов на большом уровне с камерой, которая может перемещаться и масштабироваться. Вы научитесь точно определять, по какому месту игрового мира кликнул пользователь, независимо от текущего положения камеры.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('grid', 'assets/pics/uv-grid-4096-ian-maclachlan.png');
}
create ()
{
this.add.image(0, 0, 'grid').setOrigin(0);
this.cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
camera: this.cameras.main,
left: this.cursors.left,
right: this.cursors.right,
up: this.cursors.up,
down: this.cursors.down,
acceleration: 0.02,
drag: 0.0005,
maxSpeed: 1.0
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
const cam = this.cameras.main;
cam.setBounds(0, 0, 4096, 4096);
cam.setZoom(2);
this.input.on('pointerdown', pointer => {
let p = cam.getWorldPoint(pointer.x, pointer.y);
console.log(p);
});
}
update (time, delta)
{
this.controls.update(delta);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Проблема: клик по экрану vs клик по миру
Когда вы добавляете обработчик события 'pointerdown', вы получаете объект pointer с координатами `xиy`. Эти координаты относятся к *экрану* (или холсту) и не учитывают положение камеры.
Представьте, что у вас есть большой тайлмап размером 4096x4096 пикселей, и камера показывает лишь его часть. Если игрок кликает в центре экрана, вы не можете просто использовать pointer.x и pointer.y для взаимодействия с игровым миром — вам нужно понять, какой точке в мировых координатах соответствует этот клик.
Именно эту проблему решает метод камеры getWorldPoint.
Настройка сцены и камеры
Для демонстрации создадим сцену с большой фоновой картинкой и настроим камеру.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('grid', 'assets/pics/uv-grid-4096-ian-maclachlan.png');
}
В методе create мы добавляем изображение в начало координат мира (0, 0) и настраиваем управление камерой с помощью SmoothedKeyControl. Это позволит нам перемещаться по большому полотну.
create ()
{
this.add.image(0, 0, 'grid').setOrigin(0);
// ... настройка управления камерой (cursors, controls) ...
const cam = this.cameras.main;
cam.setBounds(0, 0, 4096, 4096);
cam.setZoom(2);
}
Ключевые моменты:
- setBounds определяет границы мира, за которые камера не может выйти.
- setZoom(2) увеличивает масштаб камеры в 2 раза. Это важно, так как влияет на преобразование координат.
Обработка клика и преобразование координат
Сердце примера — обработчик события клика (pointerdown). Внутри него мы вызываем метод cam.getWorldPoint().
this.input.on('pointerdown', pointer => {
let p = cam.getWorldPoint(pointer.x, pointer.y);
console.log(p);
});
**Как это работает:**
1. pointer.x и pointer.y — это координаты клика *на экране*.
2. Метод getWorldPoint принимает эти экранные координаты и возвращает новый объект Phaser.Math.Vector2 (в консоли он отображается как объект с полями `xиy`).
3. Координаты p.x и p.y — это уже *мировые координаты* точки, в которую кликнул пользователь.
Метод автоматически учитывает все трансформации камеры: ее положение (scrollX, scrollY), масштаб (zoom) и вращение.
Практическое применение
Получив мировую точку `p`, вы можете использовать ее для множества игровых механик:
- **Создание объектов в месте клика:**
this.add.sprite(p.x, p.y, 'mySprite');
- **Проверка, попал ли клик по объекту:** Вы можете сравнить `p` с позициями и размерами ваших спрайтов.
- **Отправка юнита в точку клика (RTS):** Используйте `p` как целевую точку для движения.
- **Размещение тайлов в редакторе уровней:** Точное определение, в какую клетку сетки был произведен клик.
Без getWorldPoint все эти операции были бы гораздо сложнее, требуя ручных вычислений с учетом cam.scrollX, cam.scrollY и cam.zoom.
Что попробовать дальше
Метод cam.getWorldPoint() — это ваш надежный мост между координатами устройства ввода и координатами игрового мира. Он избавляет от необходимости вручную пересчитывать координаты, учитывая все преобразования камеры.
**Идеи для экспериментов:**
1. Попробуйте изменить cam.setZoom на лету и понаблюдайте, как меняются мировые координаты при клике в одно и то же место экрана.
2. Вместо вывода в консоль создавайте маркеры (например, маленькие кружки) в полученной точке `p`, чтобы визуализировать преобразование.
3. Исследуйте обратный метод cam.getScreenPoint(), который преобразует мировые координаты в экранные. Это может быть полезно для интерфейсов, привязанных к объектам в мире.
