О чем этот пример
Если вы разрабатываете игру для аудитории, которая использует языки с письмом справа налево (RTL), такие как арабский или иврит, вы можете столкнуться с неправильным отображением текста. Phaser предоставляет встроенную опцию `rtl` для объектов `Text`, но её использование имеет нюансы. Этот пример наглядно демонстрирует, как правильно настраивать RTL-текст и решать распространённые проблемы, связанные со смешением направлений и пунктуацией.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
create ()
{
const str1 = "این یک آزمایش است.";
// Few sentences with punctuation and numerals.
const str2 = "۱ آزمایش. 2 آزمایش، سه آزمایش & Foo آزمایش!";
// Needs implicit bidi marks to display correctly.
const str3 = "آزمایش برای Foo Ltd. و Bar Inc. باشد که آزموده شود.";
// Implicit bidi marks added; "Foo Ltd.‎ و Bar Inc.‎"
const str4 = "آزمایش برای Foo Ltd. و Bar Inc. باشد که آزموده شود.";
this.add.text(10, 10, 'Normal?!');
const t1 = this.add.text(700, 100, str1, { fontFamily: 'Arial', fontSize: 32, color: '#000000', rtl: true });
this.add.text(700, 200, str2, { fontFamily: 'Arial', fontSize: 32, color: '#000000', rtl: true });
this.add.text(700, 300, str3, { fontFamily: 'Arial', fontSize: 32, color: '#000000', rtl: true });
this.add.text(700, 400, str4, { fontFamily: 'Arial', fontSize: 32, color: '#000000', rtl: true });
let i = 0;
this.input.on('pointerdown', () => {
i++;
t1.setText(`${str1} ${i}`);
});
}
}
const config = {
type: Phaser.AUTO,
backgroundColor: '#efefef',
width: 800,
height: 600,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое RTL и зачем нужна опция в Phaser
RTL (Right-To-Left) — это направление письма, характерное для арабского, иврита и некоторых других языков. Браузеры и движки рендеринга текста должны правильно обрабатывать порядок символов и выравнивание.
В Phaser, при создании текстового объекта с помощью this.add.text(), можно передать объект стилей. Ключевое свойство rtl: true указывает движку, что текст должен обрабатываться и отрисовываться в режиме письма справа налево. Без этой настройки текст на RTL-языке может отображаться зеркально или с неправильным порядком символов.
const t1 = this.add.text(700, 100, str1, { fontFamily: 'Arial', fontSize: 32, color: '#000000', rtl: true });
Базовое использование и демонстрация строк
В примере создаётся сцена, которая отображает четыре строки текста на персидском языке (который использует арабскую вязь и является RTL). Каждая строка иллюстрирует определённый аспект.
str1 — простая фраза "Это тест.". Она корректно отобразится с опцией rtl: true.
str2 — более сложная строка, содержащая цифры (в том числе восточно-арабские "۱") и знаки препинания. Это проверка корректности обработки смешанного контента.
Все текстовые объекты позиционируются по координате X=700, что смещает их к правому краю, что логично для RTL-интерфейса, где чтение начинается справа.
const str1 = "این یک آزمایش است.";
const str2 = "۱ آزمایش. 2 آزمایش، سه آزمایش & Foo آزمایش!";
this.add.text(700, 200, str2, { fontFamily: 'Arial', fontSize: 32, color: '#000000', rtl: true });
Проблема с изоляцией и LRM-символы
Основная сложность возникает при вставке в RTL-текст фрагментов на LTR-языках (например, названий компаний на английском). В строке str3 есть "Foo Ltd. و Bar Inc.". Браузерный движок рендеринга текста может неправильно определить границы этих вставок, что приведёт к хаотичному порядку символов.
Для решения этой проблемы используются управляющие символы Unicode, явно задающие направление. В строке str4 после каждой английской аббревиатуры добавлен символ LRM (Left-to-Right Mark, `или‎` в HTML). Этот невидимый символ приказывает движку рендеринга трактовать последующий текст как LTR, корректно изолируя его от основного RTL-потока.
// Проблемная строка без явных меток
const str3 = "آزمایش برای Foo Ltd. و Bar Inc. باشد که آزموده شود.";
// Исправленная строка с LRM-символами (U+200E)
const str4 = "آزمایش برای Foo Ltd. و Bar Inc. باشد که آزموده شود.";
Динамическое обновление RTL-текста
Пример также показывает, как динамически менять текст в RTL-объекте. На сцене отслеживается событие клика (pointerdown). При каждом клике счётчик `iувеличивается, и новое значение добавляется к исходной строкеstr1в текстовом объектеt1`.
Важно: при динамической установке текста через setText() Phaser сохраняет первоначальные стили объекта, включая флаг rtl. Поэтому обновлённый текст также будет отрендерен в режиме RTL.
let i = 0;
this.input.on('pointerdown', () => {
i++;
t1.setText(`${str1} ${i}`);
});
Конфигурация игры и сцены
Код завершается стандартной для Phaser конфигурацией игры. Обратите внимание, что для корректного отображения примера важно использовать шрифт, поддерживающий необходимые символы (здесь — Arial). Фон сделан светлым (#efefef), чтобы чёрный текст хорошо читался.
const config = {
type: Phaser.AUTO,
backgroundColor: '#efefef',
width: 800,
height: 600,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Встроенная поддержка RTL в Phaser через опцию rtl: true решает базовую задачу отображения текста справа налево. Однако для профессиональной реализации, особенно при смешении языков, необходимо вручную управлять изоляцией направлений с помощью Unicode-символов, таких как LRM (U+200E). Для экспериментов попробуйте
- создать интерфейс игры с поддержкой арабского/иврита
- смешать в одной строке RTL-текст, LTR-цитаты и цифры
- реализовать динамическую подстановку имён игроков (на разных языках) в RTL-сообщения
