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

В игровых интерфейсах часто нужны элементы, которые можно перетаскивать, но только в пределах определённой зоны — например, окна настроек или инвентаря. Этот пример демонстрирует, как реализовать перетаскиваемый объект с жёсткими границами движения, используя встроенную систему событий и математические утилиты Phaser 3. Вы научитесь легко добавлять интерактивность любым игровым объектам.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


const game = new Phaser.Game({
    parent: "phaser-example",
    width: 800,
    height: 600,
    scene: {
      create: create,
    }
  })

  function create() {
    const rect1 = this.add.rectangle(400, 300, 800, 600, 0xff0000).setInteractive();
    const rect2 = this.add.rectangle(400, 300, 200, 100, 0x00ff00).setInteractive();

    this.input.setDraggable(rect2);

    rect2.on("drag", function(pointer, dragX, dragY) {
      rect2.x = Phaser.Math.Clamp(dragX, 200, 600);
      rect2.y = Phaser.Math.Clamp(dragY, 100, 400);
    });
  }

Настройка сцены и создание объектов

В начале кода мы создаём экземпляр игры Phaser и определяем функцию create, которая будет вызвана при инициализации сцены. Внутри неё подготавливаются два прямоугольника.

const game = new Phaser.Game({
    parent: "phaser-example",
    width: 800,
    height: 600,
    scene: {
      create: create,
    }
})

Первый прямоугольник (rect1) служит фоновой областью или визуальным контекстом. Второй прямоугольник (rect2) — это наш будущий перетаскиваемый элемент. Оба объекта сразу же делаются интерактивными с помощью метода .setInteractive(), что позволяет им реагировать на ввод пользователя.

function create() {
    const rect1 = this.add.rectangle(400, 300, 800, 600, 0xff0000).setInteractive();
    const rect2 = this.add.rectangle(400, 300, 200, 100, 0x00ff00).setInteractive();
}

Включение перетаскивания и обработка события

Чтобы объект можно было перетаскивать, его необходимо явно добавить в список перетаскиваемых элементов менеджера ввода с помощью метода this.input.setDraggable().

this.input.setDraggable(rect2);

После этого на объект можно подписаться на событие "drag". Это событие срабатывает непрерывно во время перемещения мыши или касания. Обработчик события получает три ключевых аргумента: pointer (объект, представляющий устройство ввода), dragX и dragY — это целевые координаты, куда система предлагает переместить объект в текущем кадре.

rect2.on("drag", function(pointer, dragX, dragY) {
    // Логика ограничения координат будет здесь
});

Ограничение движения с помощью Phaser.Math.Clamp

Если просто присвоить rect2.x = dragX, объект будет свободно перемещаться по всей сцене. Чтобы ограничить его движение заданной областью, мы используем утилиту Phaser.Math.Clamp(). Этот метод принимает три аргумента: значение, которое нужно ограничить, минимальную и максимальную границы. Он возвращает значение, гарантированно находящееся внутри этого диапазона.

rect2.x = Phaser.Math.Clamp(dragX, 200, 600);
rect2.y = Phaser.Math.Clamp(dragY, 100, 400);

В данном примере зелёный прямоугольник (rect2) может перемещаться по оси X от 200 до 600 пикселей, а по оси Y — от 100 до 400 пикселей. Это создаёт эффект, будто объект перемещается внутри невидимой коробки. Центр прямоугольника служит точкой привязки для этих вычислений.

Почему это работает именно так

Важно понимать, что событие "drag" не изменяет положение объекта автоматически. Оно лишь предоставляет предлагаемые координаты. Разработчик должен сам решить, как их применить. Такой подход даёт полный контроль над поведением: вы можете добавлять инерцию, сглаживание, привязку к сетке или, как в нашем случае, жёсткие границы.

Использование Phaser.Math.Clamp — это чистое и читаемое решение для ограничения движения. Оно избавляет от необходимости писать условные операторы if и гарантирует, что объект никогда не выйдет за пределы указанной зоны, независимо от скорости перемещения мыши.

Что попробовать дальше

Вы реализовали базовый, но мощный механизм Drag & Drop с ограничениями. Этот паттерн можно расширить: попробуйте ограничить движение областью другого игрового объекта (например, rect1), добавьте визуальную подсветку границ зоны или реализуйте привязку к сетке внутри ограничивающего прямоугольника. Эти приёмы станут основой для создания сложных и отзывчивых игровых интерфейсов.