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

Работа с физикой — ключевая часть многих игр. В Phaser Arcade Physics есть мощная система коллизий, которая позволяет не просто обнаруживать столкновения объектов, но и реагировать на них глобально. В этой статье разберем пример использования события `world.on('collide')`, которое срабатывает при любом столкновении в мире, и покажем, как это можно использовать для визуальной обратной связи или игровой логики. Этот подход особенно полезен, когда вам нужно централизованно обрабатывать последствия столкновений (например, проигрывать звуки, начислять очки или менять состояние игры), не засоряя логику каждого отдельного коллайдера.

Версия 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('atari', 'assets/sprites/atari130xe.png');
        this.load.image('ball', 'assets/sprites/shinyball.png');
        this.load.image('mushroom', 'assets/sprites/mushroom2.png');
    }

    create ()
    {
        const atari = this.physics.add.image(250, 200, 'atari')
            .setImmovable(true);

        const sprite = this.physics.add.image(400, 300, 'mushroom')
            .setVelocity(100, 200)
            .setBounce(1, 1)
            .setCollideWorldBounds(true)
            .setGravityY(200);

        // This enables the world 'collide' event, which will be detected by the collider below.
        sprite.body.onCollide = true;

        const balls = this.physics.add.staticGroup({
            key: 'ball',
            frameQuantity: 30
        });

        Phaser.Actions.PlaceOnRectangle(
            balls.getChildren(),
            new Phaser.Geom.Rectangle(84, 84, 616, 416)
        );

        // Static bodies must be updated manually if their parent game objects are moved.
        balls.refresh();

        this.physics.add.collider(sprite, atari);
        this.physics.add.collider(sprite, balls);

        this.physics.world.on('collide', (gameObject1, gameObject2, body1, body2) =>
        {
            gameObject1.setAlpha(0.5);
            gameObject2.setAlpha(0.5);
        });
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    physics: {
        default: 'arcade',
        arcade: { debug: false }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Создание физических тел и настройка коллизий

В методе create() сцены создаются основные объекты с физикой. Ключевой момент — для объекта, столкновения которого мы хотим отслеживать глобально, необходимо вручную установить флаг body.onCollide в true. Без этого флага событие 'collide' на мире не будет срабатывать для этого тела.

const sprite = this.physics.add.image(400, 300, 'mushroom')
    .setVelocity(100, 200)
    .setBounce(1, 1)
    .setCollideWorldBounds(true)
    .setGravityY(200);

// Этот флаг включает генерацию события 'collide' для данного тела.
sprite.body.onCollide = true;

Затем создаются статические объекты (группа шаров) и настраиваются коллайдеры между движущимся спрайтом и другими объектами. Коллайдеры (this.physics.add.collider) отвечают за физическую реакцию — отскок, остановку. Само по себе создание коллайдера не генерирует глобальные события.

Глобальный слушатель события столкновения

Основная магия происходит здесь. Мы подписываемся на событие 'collide' физического мира (this.physics.world). Этот обработчик будет вызываться каждый раз, когда любое тело с включенным флагом onCollide сталкивается с другим телом.

this.physics.world.on('collide', (gameObject1, gameObject2, body1, body2) =>
{
    gameObject1.setAlpha(0.5);
    gameObject2.setAlpha(0.5);
});

Колбэк-функция получает четыре аргумента: два игровых объекта (gameObject1, gameObject2) и их физические тела (body1, body2). В примере при каждом столкновении оба объекта полупрозрачными (setAlpha(0.5)). Это наглядная демонстрация момента столкновения.

Особенности работы со статическими группами

В примере используется staticGroup. Статические тела не двигаются под действием физики, но с ними могут сталкиваться динамические тела. Если вы перемещаете игровые объекты внутри статической группы программно (как здесь с помощью Phaser.Actions.PlaceOnRectangle), их физические тела необходимо обновить вручную.

// Размещаем объекты группы по прямоугольнику.
Phaser.Actions.PlaceOnRectangle(
    balls.getChildren(),
    new Phaser.Geom.Rectangle(84, 84, 616, 416)
);

// Критически важный вызов для синхронизации позиций тел с позициями спрайтов.
balls.refresh();

Без вызова refresh() физические тела останутся на исходных позициях, и коллизии будут происходить не там, где отрисованы спрайты.

Практическое применение и отладка

Событие 'collide' — это инструмент для реакций, не связанных напрямую с физикой. В реальном проекте здесь можно: * Проигрывать звук удара. * Создавать частицы (эффект искр). * Наносить урон или начислять очки. * Менять состояние объектов (например, ломать ящик).

Для отладки включите режим debug в конфигурации физики. Это покажет хитбоксы (границы тел), что поможет убедиться в правильности расстановки коллайдеров.

physics: {
    default: 'arcade',
    arcade: { debug: true } // Включаем отладочную визуализацию.
}

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

Глобальное событие 'collide' в Arcade Physics — мощный механизм для централизованной обработки столкновений. Оно отделяет логику игровой реакции от физического расчета, делая код чище и модульнее. Для экспериментов попробуйте: 1. Добавить в обработчик логику, которая по тегу (setData) или текстуре объекта определяет, что произошло (например, столкновение с монетой или врагом). 2. Реализовать одноразовое событие, которое отписывается от колбэка после первого срабатывания. 3. Использовать тело (body1 или body2) для получения дополнительной информации о столкновении, например, скорости (body.velocity).