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

При создании игр часто возникает необходимость в динамическом управлении физическими взаимодействиями. Например, нужно отключить столкновение объекта с группой после первого контакта, чтобы смоделировать "поглощение" или "отключение ловушки". В Phaser 3 Arcade Physics для этого используется метод `removeCollider`. В этой статье мы разберем готовый пример, который показывает, как удалить коллайдер во время выполнения игры прямо из коллбэка столкновения.

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

    create ()
    {
        const sprite = this.physics.add.image(400, 300, 'mushroom');

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

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

        // Static bodies must be refreshed if their game objects are moved.
        group.refresh();

        sprite
            .setVelocity(100, 200)
            .setBounce(1, 1)
            .setCollideWorldBounds(true)
            .setGravityY(200);

        const collider = this.physics.add.collider(sprite, group, null, () =>
        {
            this.physics.world.removeCollider(collider);
        });
    }
}

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

const game = new Phaser.Game(config);

Инициализация сцены и загрузка ассетов

В методе preload() загружаются два спрайта: гриб ('mushroom') и блестящий шарик ('ball'). Базовый URL указывает на репозиторий с примерами Phaser, откуда берутся изображения.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('mushroom', 'assets/sprites/mushroom2.png');
    this.load.image('ball', 'assets/sprites/shinyball.png');
}

Создание физических тел и группы

В create() создается динамическое тело спрайта гриба с помощью this.physics.add.image. Затем создается статическая группа (staticGroup) из 30 шариков. С помощью Phaser.Actions.PlaceOnRectangle все шарики размещаются по периметру прямоугольника. **Важно:** после перемещения игровых объектов в статической группе необходимо вызвать group.refresh(), чтобы обновить их физические тела.

const sprite = this.physics.add.image(400, 300, 'mushroom');

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

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

group.refresh();

Затем грибу задаются начальная скорость, упругость (отскок), включение столкновения с границами мира и гравитация по оси Y.

sprite
    .setVelocity(100, 200)
    .setBounce(1, 1)
    .setCollideWorldBounds(true)
    .setGravityY(200);

Создание и удаление коллайдера

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

Коллайдер сохраняется в константу collider. В четвертом аргументе (функция processCallback) происходит магия: мы передаем стрелочную функцию, которая вызывает this.physics.world.removeCollider(collider). Эта функция выполнится при первом же столкновении между грибом и любым шариком из группы, после чего коллайдер будет немедленно удален из физического мира. Гриб перестанет сталкиваться с шариками и будет свободно пролетать сквозь них.

const collider = this.physics.add.collider(sprite, group, null, () =>
{
    this.physics.world.removeCollider(collider);
});

Обратите внимание, что функция удаления имеет доступ к переменной collider благодаря замыканию.

Конфигурация игры и отладка

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

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

const game = new Phaser.Game(config);

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

Динамическое удаление коллайдеров — мощный инструмент для создания сложного игрового поведения. В рассмотренном примере мы удалили коллайдер при первом столкновении. Вы можете экспериментировать: удалять коллайдер после N столкновений, по таймеру, при определенном состоянии игрока или при срабатывании другого события. Также метод removeCollider можно использовать для временного отключения взаимодействий, которые позже можно восстановить, сохранив ссылку на объект коллайдера.