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

В 2D-играх спрайты часто имеют прямоугольную область для расчёта столкновений, что может выглядеть неестественно для круглых объектов. Phaser Arcade Physics позволяет задавать для спрайтов коллайдер в форме круга, что делает взаимодействие объектов более точным и визуально приятным. В этой статье мы разберём пример, где несколько шариков летят к центральному объекту, используя именно круговые коллайдеры, и объясним ключевые методы для работы с физикой.

Версия 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.spritesheet('ball', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
        this.load.image('wizball', 'assets/sprites/wizball.png');
    }

    create ()
    {
        this.cameras.main.centerOn(0, 0);

        const wizball = this.physics.add.staticImage(0, 0, 'wizball')
            .setCircle(45);

        const balls = this.physics.add.group({
            bounceX: 1,
            bounceY: 1
        });

        balls.createMultiple({
            quantity: 6,
            key: 'ball',
            frame: [ 0, 1, 2, 3, 4 ]
        });

        Phaser.Actions.PlaceOnCircle(balls.getChildren(), { x: 0, y: 0, radius: 300 });

        for (const ball of balls.getChildren())
        {
            ball.setCircle(8);
            this.physics.moveToObject(ball, wizball, 100);
        }

        this.physics.add.collider(wizball, balls);
    }
}

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);

Подготовка сцены и загрузка ассетов

Вся логика примера содержится в классе сцены Example. Метод preload отвечает за загрузку графики.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.spritesheet('ball', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
    this.load.image('wizball', 'assets/sprites/wizball.png');
}

Здесь мы устанавливаем базовый URL для загрузки и загружаем два ресурса: спрайтшит balls.png, который содержит несколько кадров шариков, и одиночное изображение wizball.png. Обратите внимание на параметры frameWidth и frameHeight для спрайтшита — они необходимы, чтобы Phaser правильно его разрезал.

Создание статического и динамических тел

В методе create происходит инициализация физических объектов. Сначала камера центрируется на точке (0,0).

create ()
{
    this.cameras.main.centerOn(0, 0);

    const wizball = this.physics.add.staticImage(0, 0, 'wizball')
        .setCircle(45);
}

Ключевой объект wizball создаётся как статическое физическое тело с помощью this.physics.add.staticImage. Статическое тело не двигается под воздействием сил или столкновений. Сразу после создания для него вызывается метод .setCircle(45). Это заменяет стандартный прямоугольный коллайдер (хитбокс) на круговой с радиусом 45 пикселей, центрированный относительно спрайта.

Далее создаётся группа (Group) динамических шариков.

const balls = this.physics.add.group({
        bounceX: 1,
        bounceY: 1
    });

    balls.createMultiple({
        quantity: 6,
        key: 'ball',
        frame: [ 0, 1, 2, 3, 4 ]
    });

Группа balls создаётся с помощью this.physics.add.group. Параметры bounceX: 1 и bounceY: 1 задают коэффициент упругости (1 означает абсолютно упругое отскок, без потери энергии). Метод createMultiple создаёт 6 спрайтов, используя загруженный спрайтшит ball. Параметр frame — это массив индексов кадров из спрайтшита, которые будут случайным образом назначены созданным шарикам, чтобы они визуально отличались.

Расстановка объектов и настройка движения

После создания шариков их нужно расположить на сцене и задать им движение.

Phaser.Actions.PlaceOnCircle(balls.getChildren(), { x: 0, y: 0, radius: 300 });

    for (const ball of balls.getChildren())
    {
        ball.setCircle(8);
        this.physics.moveToObject(ball, wizball, 100);
    }

Phaser.Actions.PlaceOnCircle — это утилита для размещения массива объектов по окружности. Мы передаём ей массив детей группы balls и конфигурацию окружности с центром в (0,0) и радиусом 300 пикселей. В результате все шарики равномерно распределяются по этому кругу.

Затем в цикле для каждого шарика выполняются две важные операции: 1. ball.setCircle(8) — так же, как и для wizball, заменяет хиктбокс шарика на круглый коллайдер с радиусом 8 пикселей. 2. this.physics.moveToObject(ball, wizball, 100) — функция, которая задаёт шарику скорость, направленную к целевому объекту (wizball). Третий аргумент (100) — это величина скорости в пикселях в секунду.

Завершение настройки: добавление коллайдера

Финальный штрих — регистрация столкновения между центральным шаром и группой шариков.

this.physics.add.collider(wizball, balls);
}

Метод this.physics.add.collider создаёт коллайдер, который будет обрабатывать столкновения между двумя объектами (или объектом и группой). Благодаря тому, что для всех объектов заданы круговые коллайдеры (setCircle), столкновения будут рассчитываться именно по кругу, а не по прямоугольнику, что обеспечивает реалистичное и точное физическое взаимодействие.

Конфигурация игры

Весь пример завершается стандартной конфигурацией игры Phaser.

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);

В конфиге указаны размеры canvas, родительский HTML-элемент и самое важное — активация физического движка Arcade с отключённым режимом отладки (debug: false). Если установить debug: true, на сцене будут отрисованы контуры коллайдеров, что очень полезно при разработке.

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

Использование метода setCircle для задания круговых коллайдеров — простой и эффективный способ значительно улучшить физику взаимодействия круглых объектов в Phaser. Это делает столкновения более предсказуемыми и естественными для игрока. Для экспериментов попробуйте изменить радиусы коллайдеров, скорость шариков, их количество или замените moveToObject на setVelocity для создания орбитального движения вокруг центрального шара.