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

При создании текстовых объектов Phaser по умолчанию вычисляет их визуальные метрики (ascent, descent, fontSize), что требует создания временного Canvas и может снижать производительность, особенно при массовом создании объектов. В этой статье мы разберем, как вручную предоставить эти метрики в конфигурации текста, полностью избежав накладных расходов на их расчет. Этот прием особенно полезен для оптимизации загрузки сцены или создания динамических интерфейсов, где текст генерируется часто.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {

        //  If you provide the Text Metrics in the Text Config then you can
        //  avoid it having to create a canvas, draw to it, then scan the canvas,
        //  in order to calculate the metrics internally.
        const config1 = {
            x: 100,
            y: 100,
            text: 'Phaser III',
            style: {
                fontSize: '48px',
                fontFamily: 'Arial',
                color: '#ffffff',
                metrics: {
                    ascent: 45,
                    descent: 10,
                    fontSize: 55
                }
            }
        };

        const text = this.make.text(config1);

        //  You can get the metrics from a Text object by doing this:

        console.log(text.getTextMetrics());

        console.log(text.toJSON());
    }
}

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

const game = new Phaser.Game(config);

В чем проблема стандартного подхода?

Когда вы создаете объект текста через this.add.text() или this.make.text(), Phaser для точного позиционирования строки вычисляет её метрики. Этот процесс включает: 1. Создание временного HTMLCanvasElement. 2. Отрисовку на нем текста с заданным стилем. 3. Анализ пикселей холста для определения реальной высоты букв (ascent) и глубины «хвостов» (descent).

Хотя для одного-двух текстовых объектов это незаметно, при создании десятков или сотней элементов (например, в больших таблицах или списках) эти операции могут создать нагрузку, особенно на мобильных устройствах.

// Стандартный способ — Phaser рассчитает метрики сам
const standardText = this.add.text(100, 200, 'Текст', { fontSize: '32px' });

Решение: предоставление метрик вручную

Phaser позволяет указать метрики напрямую в объекте стиля текста, в поле metrics. Это полностью отключает внутренний механизм расчета. Необходимо указать три ключевых свойства: - ascent: Высота букв над базовой линией (например, у «Т»). - descent: Глубина букв ниже базовой линии (например, «хвост» у «у»). - fontSize: Сумма ascent и descent. Обычно немного больше номинального размера шрифта.

Главное правило: значение fontSize в метриках должно быть равно ascent + descent. Визуально это полная высота строки текста.

const configWithMetrics = {
    x: 100,
    y: 100,
    text: 'Phaser III',
    style: {
        fontSize: '48px', // Номинальный размер для отрисовки
        fontFamily: 'Arial',
        color: '#ffffff',
        metrics: {
            ascent: 45,   // Высота над линией
            descent: 10,  // Глубина под линией
            fontSize: 55  // ascent + descent (45 + 10)
        }
    }
};

Как получить точные значения метрик?

Чтобы узнать точные метрики для вашего шрифта и размера, Phaser предоставляет удобный метод.

1. Создайте текстовый объект стандартным способом (хотя бы один раз, например, в момент предзагрузки). 2. Используйте метод .getTextMetrics() этого объекта. Он вернет объект с рассчитанными для данного текста значениями. 3. Скопируйте полученные значения ascent, descent и fontSize в ваш конфиг с метриками для последующего использования.

// 1. Создаем «эталонный» текст
const probeText = this.make.text({ x: 0, y: 0, text: 'Phaser III', style: { fontSize: '48px' } });

// 2. Получаем его метрики
const measuredMetrics = probeText.getTextMetrics();
console.log(measuredMetrics); // Выведет объект с ascent, descent, fontSize

// 3. Уничтожаем пробный объект, если он больше не нужен
probeText.destroy();

Проверка и сериализация

После создания текста с ручными метриками вы можете убедиться, что они корректно применены, и посмотреть на итоговую структуру объекта.

Метод .getTextMetrics() вернет те самые значения, которые вы указали в конфигурации. Метод .toJSON() сериализует объект текста, включая его стиль и метрики, в JSON-строку. Это полезно для отладки или сохранения состояния.

// Создаем текст с предоставленными метриками
const text = this.make.text(configWithMetrics);

// Проверяем, что метрики установлены верно
console.log(text.getTextMetrics());

// Смотрим на полную JSON-структуру объекта
console.log(text.toJSON());

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

Предоставление метрик текста вручную — это простой и эффективный прием оптимизации для сцен с большим количеством текстовых объектов. Он устраняет этап создания и анализа временного Canvas, что ускоряет инициализацию. Для экспериментов попробуйте создать систему кэширования метрик: один раз рассчитайте их для всех используемых в проекте комбинаций шрифта и размера, а затем подставляйте из заранее подготовленного словаря. Также это открывает возможность для точного вертикального выравнивания текста разных размеров без лишних вычислений.