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

При создании текстовых объектов в Phaser важно понимать, как движок определяет их размеры для корректного отображения и выравнивания. Параметр `testString`, хотя и редко упоминается в документации, играет ключевую роль в этом процессе. Эта статья объяснит его назначение на практическом примере и покажет, как его использование предотвращает "скачки" размера текстовых блоков при динамическом обновлении контента.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        this.add.text(100, 100, '⎝ example text', {
            fontSize: '48px',
            fontFamily: 'Arial',
            color: '#ffffff',
            backgroundColor: '#ff00ff',
            testString: 'abc'
        }).setStroke('#111111', 6);

        this.add.text(100, 200, '⎝ example text', {
            fontSize: '48px',
            fontFamily: 'Arial',
            color: '#ffffff',
            backgroundColor: '#ff00ff',
            testString: '⎝|MÉqgy'
        }).setStroke('#111111', 6);
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    backgroundColor: '#8E8E8E',
    scene: Example
};

const game = new Phaser.Game(config);

Что такое `testString` и зачем он нужен?

Когда Phaser создаёт объект текста с помощью this.add.text(), ему необходимо вычислить размеры (ширину и высоту) этого объекта на холсте. Для рендеринга текста движок использует Canvas API или WebGL, но перед отрисовкой нужно заранее знать, сколько места займёт строка.

Проблема возникает, когда текст динамически меняется. Если изначальная строка короткая (например, 'OK'), а потом заменяется на длинную ('Game Over'), вычисленные размеры могут резко увеличиться, что приведёт к неожиданным сдвигам в интерфейсе. Именно для решения этой проблемы существует необязательный параметр конфигурации testString.

this.add.text(x, y, 'текст', {
    fontSize: '48px',
    testString: 'abc'
});

Указав testString, вы сообщаете Phaser: "Используй эту строку как эталон для предварительного вычисления размеров текстового объекта". Движок рассчитает ширину и высоту так, как будто в объекте находится строка из testString, даже если фактический текст короче или длиннее. Это гарантирует стабильность размеров контейнера при любых изменениях текста.

Анализ примера: два текстовых объекта

Рассмотрим исходный код примера. В нём создаются два текстовых объекта с одинаковым видимым текстом и стилями, но разными значениями testString.

Первый объект использует простую тестовую строку:

this.add.text(100, 100, '⎝ example text', {
    fontSize: '48px',
    fontFamily: 'Arial',
    color: '#ffffff',
    backgroundColor: '#ff00ff',
    testString: 'abc' // Эталонная строка
}).setStroke('#111111', 6);

Второй объект в качестве testString использует более сложный набор символов:

testString: '⎝|MÉqgy' // Эталонная строка с особыми символами

Ключевое различие между строками 'abc' и '⎝|MÉqgy' — в наборе глифов (визуальных представлений символов). Вторая строка содержит: * Специальный символ `⎝` (как и в основном тексте). * Символы с верхними выносными элементами (`É`, 'q', 'g'). * Символы разной ширины.

Phaser, вычисляя размеры, будет учитывать максимальную ширину и высоту глифов из строки testString. Поэтому второй текстовый блок, скорее всего, будет иметь бóльшую расчётную высоту (из-за выносных элементов) и, возможно, ширину, даже если видимый текст идентичен первому.

Практические рекомендации по выбору testString

Как правильно подобрать значение для testString? Вот несколько практических советов:

1. **Используйте самый широкий ожидаемый символ.** Часто это заглавная 'W' или символы вроде '#'. 2. **Включайте символы с верхними и нижними выносными элементами.** Например, 'É' (или 'Å', 'Ä') для верхних и 'g', 'y', 'p' для нижних. Это обеспечит корректный учёт высоты. 3. **Учитывайте специальные символы вашего интерфейса.** Если в тексте будут встречаться иконки или нестандартные символы (как `⎝в примере), обязательно добавьте их вtestString`. 4. **Для многострочного текста добавьте пример самой длинной строки.**

Идеальная тестовая строка может выглядеть так:

testString: 'Éy|Wg'
// É - верхний выносной элемент
// y - нижний выносной элемент
// W - широкий символ
// g - нижний выносной элемент + стандартная ширина

Это гарантирует, что при смене текста внутри объекта его габариты не изменятся, а интерфейс останется стабильным. Параметр особенно полезен для элементов UI: счетчиков очков, таймеров, кнопок с меняющимся текстом.

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

Важно понимать, что testString влияет **только на внутренние расчёты размеров** текстового игрового объекта (свойства width и height). На экране отрисовывается только тот текст, который передан первым аргументом в this.add.text().

В предоставленном примере оба текстовых объекта выводят одну и ту же строку: '⎝ example text'. Разница в testString повлияет на: * Размеры фона (backgroundColor), который рассчитывается исходя из габаритов объекта. * Позиционирование объекта относительно других элементов, если оно зависит от его width или height. * Область взаимодействия (например, для ввода).

// Создаём текст
const textObject = this.add.text(100, 300, 'Hi', { testString: 'Game Over' });

// Даже если текст 'Hi', размеры будут рассчитаны для 'Game Over'
console.log(textObject.width); // Большая ширина
console.log(textObject.height); // Высота с запасом

Таким образом, вы можете видеть короткий текст, но взаимодействовать с областью, размер которой рассчитан на самый длинный возможный вариант.

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

Параметр testString — это мощный инструмент для контроля над стабильностью интерфейса в Phaser. Его правильное использование избавляет от дёрганий и смещений элементов при динамическом обновлении текста. Для экспериментов попробуйте

  1. Создать счётчик очков с testString, включающим максимальное число (например, '00000'), чтобы его ширина не менялась
  2. Построить сложное меню, где все кнопки имеют одинаковый размер благодаря правильно подобранным эталонным строкам
  3. Проверить, как меняется размер фона у двух объектов из примера, поменяв их видимый текст на что-то другое