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

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

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

Живой запуск

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

Исходный код


var config = {
    type: Phaser.CANVAS,
    parent: 'phaser-example',
    scene: {
        create: create
    }
};

var game = new Phaser.Game(config);

function create () {
    var graphics = this.add.graphics();
    graphics.lineStyle(4, 0xff00ff);
    graphics.strokeCircle(0, 0, 60);
    var container = this.add.container(400, 300, [ graphics ]);
}

Разбор исходного кода

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

var config = {
    type: Phaser.CANVAS,
    parent: 'phaser-example',
    scene: {
        create: create
    }
};

var game = new Phaser.Game(config);

function create () {
    var graphics = this.add.graphics();
    graphics.lineStyle(4, 0xff00ff);
    graphics.strokeCircle(0, 0, 60);
    var container = this.add.container(400, 300, [ graphics ]);
}

Код создает сцену (create) с использованием рендерера Canvas. Внутри функции создается объект Graphics (this.add.graphics()), настраивается стиль линии и рисуется окружность с помощью graphics.strokeCircle(0, 0, 60). Затем этот графический объект помещается в контейнер this.add.container(400, 300, [ graphics ]). На первый взгляд, все логично: контейнер позиционируется в точке (400, 300), а внутри него — окружность. Однако результат может вас удивить.

Проблема: Неявная точка отсчета

Ключевая проблема кроется в методе graphics.strokeCircle(0, 0, 60). Здесь (0, 0) — это координаты центра окружности **относительно точки отсчета самого графического объекта**.

По умолчанию точка отсчета (origin) для объекта Graphics в Phaser находится в его локальных координатах (0, 0). Когда вы рисуете окружность с центром в (0, 0), она фактически рисуется прямо в этой точке отсчета.

Контейнер же позиционирует свои дочерние элементы относительно своей собственной точки отсчета (которая тоже по умолчанию (0, 0)). В итоге, координаты окружности (0, 0) привязываются к точке отсчета контейнера, что приводит к неожиданному смещению. Вместо того чтобы окружность была центрирована внутри контейнера, она рисуется так, будто ее центр совпадает с позицией контейнера на сцене, что выглядит как "сдвиг".

Решение: Корректировка позиции или точки отсчета

Чтобы исправить позиционирование, нужно сместить саму графику внутри контейнера. Есть несколько подходов:

1. **Смещение позиции графики:** Установите позицию графического объекта так, чтобы его содержимое было видно правильно внутри контейнера.

function create () {
    var graphics = this.add.graphics();
    graphics.lineStyle(4, 0xff00ff);
    graphics.strokeCircle(0, 0, 60);
    var container = this.add.container(400, 300, [ graphics ]);
    // Смещаем графику внутри контейнера
    graphics.x = 0;
    graphics.y = 0;
}

2. **Использование метода setPosition:** Более явный способ задания позиции.

function create () {
    var graphics = this.add.graphics();
    graphics.lineStyle(4, 0xff00ff);
    graphics.strokeCircle(0, 0, 60);
    var container = this.add.container(400, 300, [ graphics ]);
    // Устанавливаем позицию графики относительно контейнера
    graphics.setPosition(0, 0);
}

3. **Рисование с учетом смещения:** Если вы хотите, чтобы окружность была центрирована в контейнере, можно нарисовать ее с центром в (0, 0), но это требует понимания, что точка отсчета графики уже находится в (0, 0).

В данном примере, поскольку окружность уже нарисована с центром в (0, 0), и контейнер расположен в (400, 300), окружность будет отображаться с центром в этой точке сцены, а не внутри контейнера как ожидалось. Корректировка позиции графики внутри контейнера решает проблему.

Лучшие практики работы с графикой и контейнерами

Чтобы избежать подобных ошибок в будущем, следуйте этим рекомендациям:

- **Всегда задавайте явную позицию для графики:** При создании графических объектов внутри контейнеров устанавливайте их `xиy` свойства, чтобы контролировать положение относительно контейнера. - **Используйте setOrigin для графики:** Если вам нужно изменить точку отсчета графического объекта, используйте graphics.setOrigin(0.5, 0.5) для центрирования, но помните, что это повлияет на все последующие рисования. - **Проверяйте координаты в отладчике:** Phaser предоставляет инструменты для визуализации границ и точек отсчета, что упрощает отладку. - **Документируйте систему координат:** В сложных сценах с множеством контейнеров задокументируйте, какие координаты используются (локальные или глобальные), чтобы не запутаться.

Пример с явным позиционированием:

function create () {
    var graphics = this.add.graphics();
    graphics.lineStyle(4, 0xff00ff);
    // Рисуем окружность с центром в (0, 0)
    graphics.strokeCircle(0, 0, 60);
    // Создаем контейнер и сразу позиционируем графику внутри него
    var container = this.add.container(400, 300, [ graphics ]);
    graphics.x = 0;
    graphics.y = 0;
}

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

Позиционирование графики в контейнерах Phaser требует внимания к точкам отсчета и локальным координатам. Основной вывод: всегда явно задавайте позицию графических объектов внутри контейнеров, чтобы избежать неожиданных смещений. Для экспериментов попробуйте: - Создать анимированную окружность, которая движется внутри контейнера. - Использовать несколько графических объектов в одном контейнере с разными позициями. - Поиграть с методом setOrigin для графики и увидеть, как меняется отображение. Эти практики помогут вам создавать более предсказуемые и управляемые визуальные элементы в играх.