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

В создании игр текст часто играет ключевую роль — от интерфейсов до диалоговых окон. Но когда текст слишком длинный для вашего дизайна, он может "сломать" визуальную композицию. В Phaser есть два основных типа текста: обычный `Text` и `BitmapText`. В этой статье мы разберем, как эффективно управлять шириной текста, особенно для `BitmapText`, используя метод `setMaxWidth`. Это позволит вам автоматически переносить строки и сохранять текст в заданных границах, что критически важно для создания чистого и профессионального игрового интерфейса.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload() {
        this.load.bitmapFont('gothic', 'https://labs.phaser.io/assets/fonts/bitmap/gothic.png', 'https://labs.phaser.io/assets/fonts/bitmap/gothic.xml');
    }

    create() {
        this.graphics = this.add.graphics({ x: 0, y: 0, fillStyle: { color: 0xff00ff, alpha: 1 } });
        // Text
        this.text = this.add.text(100, 100, 'Hello Phaser', { fontFamily: 'Arial Black', fontSize: 32 });
        this.text.style.setWordWrapWidth(30);

        // Bitmap Text 
        const bitmapText = this.add.bitmapText(100, 400, 'gothic', 'Hello Phaser', 32);
        bitmapText.setMaxWidth(30);
        this.bounds2 = bitmapText.getTextBounds(true);
    }

    update ()
    {
        this.graphics.clear();

        this.graphics.lineStyle(1, 0xff0000, 1);
        this.graphics.strokeRectShape(this.text.getBounds());

        this.graphics.fillRect(this.bounds2.global.x, this.bounds2.global.y, this.bounds2.global.width, this.bounds2.global.height);
    }
}

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

const game = new Phaser.Game(config);

Два типа текста: Text и BitmapText

Phaser предлагает два класса для работы с текстом: Text и BitmapText. Они имеют разную внутреннюю реализацию и набор методов.

Text использует системные или веб-шрифты и рендерится динамически. Он поддерживает множество стилей и эффектов через CSS. Для управления переносом строк в этом объекте используется метод setWordWrapWidth.

this.text = this.add.text(100, 100, 'Hello Phaser', { fontFamily: 'Arial Black', fontSize: 32 });
this.text.style.setWordWrapWidth(30);

BitmapText использует заранее подготовленные текстовые изображения (bitmap fonts). Это часто дает лучшую производительность и единый визуальный стиль на разных устройствах, но ограничивает в динамических стилях. Для управления шириной в BitmapText используется метод setMaxWidth.

const bitmapText = this.add.bitmapText(100, 400, 'gothic', 'Hello Phaser', 32);
bitmapText.setMaxWidth(30);

Как работает setMaxWidth для BitmapText

Метод setMaxWidth устанавливает максимальную ширину области, в которую должен поместиться текст. Если исходная строка текста (без переносов) превышает эту ширину, Phaser автоматически разбивает текст на несколько строк, используя переносы по словам.

Важно понимать, что значение setMaxWidth задается в пикселях. Это не количество символов, а физическая ширина области отрисовки. Алгоритм переноса пытается разбить строку на слова, которые не превышают эту ширину.

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

this.bounds2 = bitmapText.getTextBounds(true);

Аргумент true в getTextBounds указывает, что нужно рассчитать границы немедленно (иногда расчет может быть отложенным).

Визуализация границ текста для понимания

Чтобы точно понять, как текст размещается после применения setMaxWidth, полезно визуализировать его границы. В примере это делается в методе update с помощью объекта Graphics.

Для обычного Text мы получаем его общие геометрические границы методом getBounds и рисуем красный контур.

this.graphics.lineStyle(1, 0xff0000, 1);
this.graphics.strokeRectShape(this.text.getBounds());

Для BitmapText мы используем заранее вычисленные границы (bounds2) и заполняем эту область прямоугольником цвета 0xff00ff.

this.graphics.fillRect(this.bounds2.global.x, this.bounds2.global.y, this.bounds2.global.width, this.bounds2.global.height);

Это позволяет увидеть реальную область, занимаемую текстом после переноса строк, и убедиться, что она соответствует ожидаемой ширине.

Практические советы и важные детали

При использовании setMaxWidth для BitmapText следует учитывать несколько ключевых моментов:

1. **Загрузка шрифта**: BitmapFont должен быть заранее загружен через load.bitmapFont. Шрифт состоит из изображения (.png) и файла данных (.xml), описывающего символы.

this.load.bitmapFont('gothic', 'https://labs.phaser.io/assets/fonts/bitmap/gothic.png', 'https://labs.phaser.io/assets/fonts/bitmap/gothic.xml');

2. **Размер шрифта**: При создании bitmapText вы указываете размер (в примере — 32). Это размер, заданный в данных шрифта, и он влияет на то, как будет рассчитываться ширина.

3. **Обновление текста**: Если вы динамически изменяете текст объекта (например, bitmapText.setText('Новый текст')), перенос строк будет автоматически пересчитан на основе установленной setMaxWidth. Не нужно вызывать метод повторно.

4. **Производительность**: BitmapText обычно рендерится быстрее, чем Text, особенно при большом количестве текстовых элементов. Использование setMaxWidth не добавляет значительных затрат.

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

Метод setMaxWidth для BitmapText — это мощный и простой инструмент для контроля над layout текста в вашей игре. Он позволяет автоматически форматировать длинные строки, сохраняя интерфейс чистым и адаптивным. Для экспериментов попробуйте: динамически изменять значение setMaxWidth в ответ на действия игрока (например, при изменении размеров окна); комбинировать перенос строк с другими эффектами BitmapText, такими как окрашивание; сравнить производительность и визуальный результат между Text с setWordWrapWidth и BitmapText с setMaxWidth в сложных сценах.