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

Разработка игр с физикой — это не только логика взаимодействий, но и постоянная борьба с невидимыми силами. Отладка столкновений, проверка свойств тел и контроль за соединениями могут превратиться в долгий процесс угадывания. Встроенный debug-режим Matter.js в Phaser 3 позволяет визуализировать физический мир, экономя часы разработки. Эта статья покажет, как настроить и использовать debug-отрисовку для анализа статических и динамических тел, ограничений (joints), скоростей и коллизий. Вы научитесь быстро диагностировать проблемы и точечно включать только нужные визуальные подсказки.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    create ()
    {
        this.matter.world.setBounds();

        //  First, we'll create a few static bodies
        const body1 = this.matter.add.rectangle(250, 50, 200, 32, { isStatic: true });
    
        this.matter.add.polygon(600, 100, 3, 40, { isStatic: true });
        this.matter.add.polygon(100, 500, 8, 50, { isStatic: true });
        this.matter.add.rectangle(750, 200, 16, 180, { isStatic: true });
    
        //  Now a body that shows off internal edges + convex hulls
        const star = '50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38';
    
        this.matter.add.fromVertices(700, 500, star, { restitution: 0.5 }, true);
    
        //  Some different joint types
        const body2 = this.matter.add.circle(150, 250, 16);
        const body3 = this.matter.add.circle(400, 450, 16);
        const body4 = this.matter.add.circle(500, 50, 16);
        
        //  A spring, because length > 0 and stiffness < 0.9
        this.matter.add.spring(body1, body2, 140, 0.001);
    
        //  A joint, because length > 0 and stiffness > 0.1
        this.matter.add.worldConstraint(body3, 140, 1, { pointA: { x: 400, y: 250 }});
    
        //  A pin, because length = 0 and stiffness > 0.1
        this.matter.add.worldConstraint(body4, 0, 1, { pointA: { x: 500, y: 50 }});
    
        //  Finally some random dynamic bodies
        for (let i = 0; i < 12; i++)
        {
            let x = Phaser.Math.Between(100, 700);
            let y = Phaser.Math.Between(100, 500);
    
            if (Math.random() < 0.5)
            {
                let sides = Phaser.Math.Between(3, 14);
                let radius = Phaser.Math.Between(8, 50);
    
                this.matter.add.polygon(x, y, sides, radius, { restitution: 0.5 });
            }
            else
            {
                let width = Phaser.Math.Between(16, 128);
                let height = Phaser.Math.Between(8, 64);
    
                this.matter.add.rectangle(x, y, width, height, { restitution: 0.5 });
            }
        }
    
        this.matter.add.mouseSpring();
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    physics: {
        default: 'matter',
        matter: {
            enableSleeping: true,
            debug: {

                showAxes: false,
                showAngleIndicator: true,
                angleColor: 0xe81153,

                showBroadphase: false,
                broadphaseColor: 0xffb400,

                showBounds: false,
                boundsColor: 0xffffff,

                showVelocity: true,
                velocityColor: 0x00aeef,

                showCollisions: true,
                collisionColor: 0xf5950c,
    
                showSeparations: false,
                separationColor: 0xffa500,

                showBody: true,
                showStaticBody: true,
                showInternalEdges: true,

                renderFill: false,
                renderLine: true,
    
                fillColor: 0x106909,
                fillOpacity: 1,
                lineColor: 0x28de19,
                lineOpacity: 1,
                lineThickness: 1,
    
                staticFillColor: 0x0d177b,
                staticLineColor: 0x1327e4,

                showSleeping: true,
                staticBodySleepOpacity: 1,
                sleepFillColor: 0x464646,
                sleepLineColor: 0x999a99,
    
                showSensors: true,
                sensorFillColor: 0x0d177b,
                sensorLineColor: 0x1327e4,
    
                showPositions: true,
                positionSize: 4,
                positionColor: 0xe042da,
    
                showJoint: true,
                jointColor: 0xe0e042,
                jointLineOpacity: 1,
                jointLineThickness: 2,
    
                pinSize: 4,
                pinColor: 0x42e0e0,
    
                springColor: 0xe042e0,
    
                anchorColor: 0xefefef,
                anchorSize: 4,
    
                showConvexHulls: true,
                hullColor: 0xd703d0
            }
        }
    },
    scene: Example
};

let game = new Phaser.Game(config);

Включение и базовая настройка debug-режима

Для активации визуальной отладки необходимо в конфигурации игры, внутри раздела physics.matter, установить объект debug. Именно в нем перечисляются все возможные опции отрисовки. Включение режима происходит автоматически, если этот объект присутствует.

physics: {
    default: 'matter',
    matter: {
        enableSleeping: true,
        debug: {
            // Все опции отрисовки указываются здесь
            showBody: true,
            renderLine: true
        }
    }
}

Ключевой параметр showBody включает отрисовку всех физических тел. renderLine определяет, что тела будут обведены контуром, а не залиты цветом. Это базовые настройки, которые делают физические формы видимыми на экране.

Визуализация свойств физических тел

Debug-режим позволяет отображать не только форму тела, но и его внутреннее состояние. Это критически важно для понимания поведения объектов в реальном времени.

debug: {
    showAngleIndicator: true,
    angleColor: 0xe81153,
    showVelocity: true,
    velocityColor: 0x00aeef,
    showPositions: true,
    positionSize: 4,
    positionColor: 0xe042da
}

* showAngleIndicator: Рисует дугу, показывающую угол поворота тела. Цвет задается angleColor. * showVelocity: Отображает вектор текущей скорости каждого тела. Длина линии соответствует скорости, направление — вектору движения. * showPositions: Помечает центр масс (anchor) каждого тела точкой. Ее размер и цвет настраиваются positionSize и positionColor.

Эти опции превращают абстрактные физические свойства в понятные графические метки.

Отладка коллизий и внутренней структуры

Сложные составные тела и многоугольники требуют особого внимания. Matter.js предоставляет инструменты для отладки их геометрии.

debug: {
    showCollisions: true,
    collisionColor: 0xf5950c,
    showInternalEdges: true,
    showConvexHulls: true,
    hullColor: 0xd703d0
}

* showCollisions: Подсвечивает области активных столкновений между телами. Это самый прямой способ увидеть, где происходит соприкосновение. * showInternalEdges: Показывает внутренние ребра у составных тел (созданных через fromVertices). Без этой опции такое тело может отображаться как выпуклая оболочка. * showConvexHulls: Отрисовывает выпуклую оболочку для составных тел. Полезно для понимания, как физический движок "видит" сложную форму.

На примере звезды (fromVertices) включение showInternalEdges и showConvexHulls наглядно демонстрирует разницу между ее визуальной формой и физическим представлением.

Статические тела, сенсоры и режим сна

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

debug: {
    showStaticBody: true,
    staticLineColor: 0x1327e4,
    showSensors: true,
    sensorLineColor: 0x1327e4,
    showSleeping: true,
    sleepLineColor: 0x999a99
}

* showStaticBody и staticLineColor: Контролируют отображение статических тел (с isStatic: true). Их можно окрасить в отличный от динамических цвет. * showSensors: Визуализирует сенсоры (тела с isSensor: true), которые обнаруживают пересечения, но не имеют физического ответа. * showSleeping: Показывает тела, перешедшие в режим сна (когда они неподвижны). Цвет таких тел меняется на sleepLineColor и sleepFillColor. Это помогает понять, какие объекты больше не участвуют в расчетах.

Визуализация соединений: пружины, шарниры, пины

Соединения (joints/constraints) — мощный инструмент Matter.js. Их отладка без визуализации почти невозможна.

debug: {
    showJoint: true,
    jointColor: 0xe0e042,
    springColor: 0xe042e0,
    pinSize: 4,
    pinColor: 0x42e0e0
}
В примере создаются три типа связей между телами:
1.  **Пружина (Spring)**: Создается методом `this.matter.add.spring`. В debug-режиме отображается линией цвета `springColor`.
2.  **Шарнир/Ограничение (Joint)**: Создается `this.matter.add.worldConstraint` с длиной > 0 и высокой жесткостью (`stiffness: 1`). Рисуется цветом `jointColor`.
3.  **Пин (Pin)**: Тот же `worldConstraint`, но с нулевой длиной (`length: 0`). В точке крепления отображается маркер, заданный `pinSize` и `pinColor`.

Опция showJoint отвечает за отрисовку всех типов соединений.

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

Debug-режим Matter.js — это ваш рентгеновский аппарат для физического мира игры. Настраивая его, вы переходите от предположений к точным данным о коллизиях, скоростях и связях. Начните с включения showBody, showVelocity и showCollisions для общей картины, затем добавляйте специфичные опции вроде showInternalEdges или showSleeping по мере необходимости. Для экспериментов попробуйте: 1. Создать сложный уровень со статической геометрией и включить только showStaticBody и showConvexHulls. 2. Построить цепь или маятник с помощью worldConstraint и наблюдать за шарнирами с showJoint: true. 3. Поиграть с цветами (lineColor, fillColor) для создания собственной, интуитивно понятной цветовой схемы отладки под стиль вашей игры.