О чем этот пример

При разработке игр часто возникает необходимость работать с разными системами координат. Игровые объекты живут в мировых координатах, а пользователь взаимодействует с экраном через координаты указателя. 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(), который преобразует мировые координаты в экранные. Это может быть полезно для интерфейсов, привязанных к объектам в мире.