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

Корректное отображение текста — ключевая часть интерфейса любой игры. Когда текст выходит за границы элемента или экрана, он становится нечитаемым и портит впечатление. Phaser предлагает встроенную систему переноса текста по ширине, которая работает в двух режимах: базовом и расширенном (`useAdvancedWrap`). Эта статья покажет, как легко управлять переносом текста в вашем проекте, чтобы диалоги, описания и интерфейс всегда выглядели аккуратно.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        // Basic text wrapping based on width.
        this.make.text({
            x: 400,
            y: 100,
            text: 'The sky above the port was the color of television, tuned to a dead channel.',
            origin: { x: 0.5, y: 0.5 },
            style: {
                font: 'bold 25px Arial',
                fill: 'white',
                wordWrap: { width: 300 }
            }
        });

        // Basic wrap will NOT touch the whitespace in your text.
        this.make.text({
            x: 400,
            y: 250,
            text: '        Basic wrapping:        look        at        all        this        weird         space        ',
            origin: { x: 0.5, y: 0.5 },
            style: {
                font: 'bold 25px Arial',
                fill: 'white',
                wordWrap: { width: 300 }
            }
        });

        // Advanced wrap will collapse neighboring spaces into a single space and trim whitespace from
        // the start and end of each line.
        this.make.text({
            x: 400,
            y: 375,
            text: '        Advanced wrapping:        space        collapses        and        is        trimmed        ',
            origin: { x: 0.5, y: 0.5 },
            style: {
                font: 'bold 25px Arial',
                fill: 'white',
                wordWrap: { width: 300, useAdvancedWrap: true }
            }
        });

        // The advanced word wrapping algorithm will also break words that are longer than the specified
        // wrap width
        this.make.text({
            x: 400,
            y: 500,
            text: 'Long word incoming: Supercalifragilisticexpialidocious!',
            origin: { x: 0.5, y: 0.5 },
            style: {
                font: 'bold 25px Arial',
                fill: 'white',
                wordWrap: { width: 300, useAdvancedWrap: true }
            }
        });
    }
}

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

const game = new Phaser.Game(config);

Создание текста с базовым переносом

Базовый перенос — это самый простой способ ограничить ширину текстового блока. Вы указываете максимальную ширину (width) в пикселях, и движок автоматически разбивает строку, когда она превышает этот лимит. Разрыв происходит только по границам слов (пробелам).

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

this.make.text({
    x: 400,
    y: 100,
    text: 'The sky above the port was the color of television, tuned to a dead channel.',
    origin: { x: 0.5, y: 0.5 },
    style: {
        font: 'bold 25px Arial',
        fill: 'white',
        wordWrap: { width: 300 } // Базовый перенос на ширину 300px
    }
});

Ограничение базового переноса

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

В примере ниже текст содержит множество пробелов. Базовый перенос разобьёт строку по ширине, но все эти пробелы останутся видимыми, создавая странные отступы в начале строк и между словами.

this.make.text({
    x: 400,
    y: 250,
    text: '        Basic wrapping:        look        at        all        this        weird         space        ',
    origin: { x: 0.5, y: 0.5 },
    style: {
        font: 'bold 25px Arial',
        fill: 'white',
        wordWrap: { width: 300 } // Пробелы сохраняются
    }
});

Расширенный перенос с useAdvancedWrap

Для более «интеллектуальной» обработки текста используйте расширенный режим, активируемый флагом useAdvancedWrap: true. Этот режим выполняет предварительную очистку текста: он обрезает пробелы в начале и конце каждой итоговой строки, а также заменяет несколько идущих подряд пробелов на один.

Это идеально подходит для текста, который может быть загружен из внешнего источника (например, файла локализации) и содержать непреднамеренное форматирование.

this.make.text({
    x: 400,
    y: 375,
    text: '        Advanced wrapping:        space        collapses        and        is        trimmed        ',
    origin: { x: 0.5, y: 0.5 },
    style: {
        font: 'bold 25px Arial',
        fill: 'white',
        wordWrap: { width: 300, useAdvancedWrap: true } // Пробелы схлопнутся и обрежутся
    }
});

Перенос слишком длинных слов

Ещё одно ключевое преимущество расширенного режима — умение разбивать слишком длинные слова, которые не помещаются в заданную ширину. Базовый перенос оставит такое слово целиком на одной строке, что нарушит ограничение по ширине.

Расширенный алгоритм принудительно разрывает длинное слово по символам, чтобы оно вписалось в лимит width. Это критически важно для интерфейсов с динамическим текстом, где вы не можете заранее проверить длину всех слов (например, имена игроков или локаций).

this.make.text({
    x: 400,
    y: 500,
    text: 'Long word incoming: Supercalifragilisticexpialidocious!',
    origin: { x: 0.5, y: 0.5 },
    style: {
        font: 'bold 25px Arial',
        fill: 'white',
        wordWrap: { width: 300, useAdvancedWrap: true } // Длинное слово будет разбито
    }
});

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

Phaser предоставляет два инструмента для переноса текста: простой (wordWrap: { width }) и продвинутый (wordWrap: { width, useAdvancedWrap: true }). Для большинства игровых интерфейсов, где текст генерируется динамически, рекомендуется сразу использовать расширенный режим — он обеспечивает чистый и предсказуемый результат. Поэкспериментируйте, применив перенос к тексту в диалоговых окнах, описаниях предметов или всплывающих подсказках. Попробуйте динамически менять свойство wordWrap.width в ответ на изменение размера игрового окна для создания адаптивных интерфейсов.