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

Визуализация — ключевая часть любой игры. Иногда стандартных спрайтов недостаточно, и нужны динамически создаваемые фигуры. В Phaser для этого есть мощный объект `Graphics`, позволяющий рисовать примитивы прямо во время выполнения. В этой статье мы разберем, как с его помощью создавать дуги и секторы — базовые элементы для индикаторов здоровья, радиолокационных экранов или нестандартных эффектов. Мы начнем с простого примера, который рисует два закрашенных сегмента дуги, и подробно изучим каждый параметр метода `arc()`. Вы поймете, как контролировать форму, направление и сглаживание дуг, чтобы использовать их в своих проектах.

Версия 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('metal', 'assets/textures/alien-metal.jpg');
        this.load.image('bunny', 'assets/sprites/bunny.png');
    }

    create ()
    {
        const graphics = this.add.graphics();

        graphics.fillStyle(0x00ff00, 1);

        graphics.beginPath();
        graphics.arc(200, 300, 100, Phaser.Math.DegToRad(0), Phaser.Math.DegToRad(280), false, 0.01);
        graphics.fillPath();
        graphics.closePath();

        graphics.beginPath();
        graphics.arc(400, 300, 100, Phaser.Math.DegToRad(0), Phaser.Math.DegToRad(280), true, 0.01);
        graphics.fillPath();
        graphics.closePath();
    }
}

const config = {
    width: 800,
    height: 600,
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка сцены и создание графического объекта

Перед тем как рисовать, нужно подготовить сцену и создать холст для рисования — объект Graphics. В нашем примере загрузка ассетов в preload() не влияет на рисование дуг, она добавлена для демонстрации общего контекста.

Ключевой шаг происходит в методе create(), где создается графический объект. Именно он будет хранить все инструкции для отрисовки.

const graphics = this.add.graphics();

Сразу после создания мы задаем цвет заливки с помощью fillStyle(). Первый параметр — цвет в HEX-формате (0x00ff00 — зеленый), второй — альфа-канал (прозрачность от 0 до 1).

graphics.fillStyle(0x00ff00, 1);

Анатомия метода arc(): от углов до сглаживания

Метод arc() объекта Graphics — это основной инструмент для рисования дуг. Его сигнатура содержит несколько важных параметров, которые определяют результат.

graphics.arc(x, y, radius, startAngle, endAngle, anticlockwise, overshoot);

Разберем их по порядку: * `x,y` — координаты центра дуги на холсте. * radius — радиус дуги в пикселях. * startAngle, endAngle — начальный и конечный угол дуги в радианах. Именно здесь в игру входит полезный хелпер Phaser.Math.DegToRad(), который переводит привычные градусы в радианы. Например, 280 градусов превращаются в нужное для API число. * anticlockwise — логический параметр. Если false (по умолчанию), дуга рисуется по часовой стрелке от начального угла к конечному. Если true — против часовой стрелки. * overshoot — параметр сглаживания (антиалиасинга) для контура дуги. Меньшее значение (например, 0.01) дает более гладкую, но ресурсоемкую кривую.

Рисование пути: beginPath, fillPath и closePath

В Phaser, как и в стандартном Canvas API, рисование выполняется с помощью концепции путей (paths). Последовательность команд строгая.

1. beginPath() — объявляет начало нового контура. Все последующие команды рисования (как arc()) будут относиться к этому пути. 2. Далее вызывается arc() с нужными параметрами, чтобы описать форму. 3. fillPath() — ключевая команда, которая заливает текущий описанный путь установленным ранее цветом (fillStyle). Без этого вызова дуга будет описана, но не видна на экране. 4. closePath() — формально завершает путь. Важно отметить, что для залитой фигуры эта команда не соединяет начальную и конечную точки линией (так как заливка и так сплошная), но является хорошей практикой для чистоты кода.

Вот как выглядит полный цикл для первой дуги:

graphics.beginPath();
graphics.arc(200, 300, 100, Phaser.Math.DegToRad(0), Phaser.Math.DegToRad(280), false, 0.01);
graphics.fillPath();
graphics.closePath();

Сравнение: дуга по часовой стрелке и против

В примере рисуются две дуги с одинаковыми центрами, радиусами и углами (от 0 до 280 градусов), но с разными значениями параметра anticlockwise. Это наглядно демонстрирует его влияние.

Первая дуга рисуется с anticlockwise: false. Дуга начинается на "3 часа" (0 радиан) и идет по часовой стрелке, останавливаясь чуть раньше полного круга. Получается закрашенный сегмент, похожий на недорисованный круг.

// Дуга по часовой стрелке (неполный круг снизу)
graphics.arc(200, 300, 100, Phaser.Math.DegToRad(0), Phaser.Math.DegToRad(280), false, 0.01);

Вторая дуга, с anticlockwise: true, начинает движение из той же точки, но против часовой стрелки. Чтобы достичь того же конечного угла (280 градусов), ей приходится пройти почти весь круг в обратном направлении. Визуально это создает гораздо больший закрашенный сегмент.

// Дуга против часовой стрелки (большой сегмент сверху)
graphics.arc(400, 300, 100, Phaser.Math.DegToRad(0), Phaser.Math.DegToRad(280), true, 0.01);

Этот прием полезен для создания индикаторов обратного отсчета или заполнения, где направление имеет значение.

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

Объект Graphics и метод arc() открывают прямой путь к созданию динамической векторной графики в Phaser. Вы научились описывать дуги, контролируя их размер, угол и направление обхода. Для экспериментов попробуйте: 1. Анимировать параметр endAngle, чтобы создать плавно заполняющийся круговой индикатор. 2. Комбинировать несколько дуг с разными цветами (fillStyle) в одном Graphics объекте для создания сложных диаграмм. 3. Использовать lineStyle() перед beginPath() и strokePath() вместо fillPath(), чтобы нарисовать только контур дуги для других визуальных задач.