О чем этот пример
Оживление текста в игре — ключ к вовлечению игрока. В этой статье мы разберем пример из официальной коллекции Phaser, который демонстрирует, как превратить статичные текстовые элементы в интерактивные объекты, реагирующие на наведение курсора мыши. Вы научитесь создавать группы объектов, назначать им интерактивные области и обрабатывать события ввода, что полезно для меню, подсказок или любых элементов интерфейса, требующих отклика.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
nouns = [ 'ninja', 'chair', 'pancake', 'statue', 'unicorn', 'rainbows', 'laser', 'senor', 'bunny', 'captain', 'nibblets', 'cupcake', 'carrot', 'gnomes', 'glitter', 'potato', 'salad', 'toejam', 'curtains', 'beets', 'toilet', 'exorcism', 'stick figures', 'mermaid eggs', 'sea barnacles', 'dragons', 'jellybeans', 'snakes', 'dolls', 'bushes', 'cookies', 'apples', 'ice cream', 'ukulele', 'kazoo', 'banjo', 'opera singer', 'circus', 'trampoline', 'carousel', 'carnival', 'locomotive', 'hot air balloon', 'praying mantis', 'animator', 'artisan', 'artist', 'colorist', 'inker', 'coppersmith', 'director', 'designer', 'flatter', 'stylist', 'leadman', 'limner', 'make-up artist', 'model', 'musician', 'penciller', 'producer', 'scenographer', 'set decorator', 'silversmith', 'teacher', 'auto mechanic', 'beader', 'bobbin boy', 'clerk of the chapel', 'filling station attendant', 'foreman', 'maintenance engineering', 'mechanic', 'miller', 'moldmaker', 'panel beater', 'patternmaker', 'plant operator', 'plumber', 'sawfiler', 'shop foreman', 'soaper', 'stationary engineer', 'wheelwright', 'woodworkers' ];
adjectives = [ 'adamant', 'adroit', 'amatory', 'animistic', 'antic', 'arcadian', 'baleful', 'bellicose', 'bilious', 'boorish', 'calamitous', 'caustic', 'cerulean', 'comely', 'concomitant', 'contumacious', 'corpulent', 'crapulous', 'defamatory', 'didactic', 'dilatory', 'dowdy', 'efficacious', 'effulgent', 'egregious', 'endemic', 'equanimous', 'execrable', 'fastidious', 'feckless', 'fecund', 'friable', 'fulsome', 'garrulous', 'guileless', 'gustatory', 'heuristic', 'histrionic', 'hubristic', 'incendiary', 'insidious', 'insolent', 'intransigent', 'inveterate', 'invidious', 'irksome', 'jejune', 'jocular', 'judicious', 'lachrymose', 'limpid', 'loquacious', 'luminous', 'mannered', 'mendacious', 'meretricious', 'minatory', 'mordant', 'munificent', 'nefarious', 'noxious', 'obtuse', 'parsimonious', 'pendulous', 'pernicious', 'pervasive', 'petulant', 'platitudinous', 'precipitate', 'propitious', 'puckish', 'querulous', 'quiescent', 'rebarbative', 'recalcitant', 'redolent', 'rhadamanthine', 'risible', 'ruminative', 'sagacious', 'salubrious', 'sartorial', 'sclerotic', 'serpentine', 'strident', 'taciturn', 'tenacious', 'tremulous', 'trenchant', 'turbulent', 'turgid', 'ubiquitous', 'uxorious', 'verdant', 'voluble', 'voracious', 'wheedling', 'withering', 'zealous' ];
create ()
{
const group = this.add.group();
group.classType = Phaser.GameObjects.Text;
// Create some random Text strings
for (let i = 0; i < 32; i++)
{
const x = Phaser.Math.Between(-100, 700);
const y = Phaser.Math.Between(0, 550);
const str = `${Phaser.Math.RND.pick(this.adjectives)} ${Phaser.Math.RND.pick(this.nouns)}`;
const font = { font: '24px Arial' };
const text = group.create(x, y, str, font);
text.setInteractive(new Phaser.Geom.Rectangle(0, 0, text.width, text.height), Phaser.Geom.Rectangle.Contains);
}
// Input Event listeners
this.input.on('gameobjectover', (pointer, gameObject) =>
{
gameObject.setTint(0xff0000, 0xff0000, 0xffff00, 0xff00ff);
});
this.input.on('gameobjectout', (pointer, gameObject) =>
{
gameObject.clearTint();
});
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка данных и создание сцены
Класс Example наследуется от Phaser.Scene и содержит два массива: nouns (существительные) и adjectives (прилагательные). Эти массивы служат источником данных для генерации случайных текстовых фраз.
В методе create() мы создаем основу для работы с текстом. Сначала инициализируется группа игровых объектов с помощью this.add.group(). Группы в Phaser позволяют эффективно управлять коллекциями однотипных объектов.
const group = this.add.group();
group.classType = Phaser.GameObjects.Text;
Установка classType указывает, что все объекты, создаваемые в этой группе, будут экземплярами Phaser.GameObjects.Text. Это позволяет использовать метод group.create() для мгновенного добавления текстовых объектов.
Генерация случайных текстовых объектов
В цикле создается 32 текстовых объекта со случайными параметрами. Для каждого объекта определяется случайная позиция на экране и генерируется строка, состоящая из случайного прилагательного и существительного.
const x = Phaser.Math.Between(-100, 700);
const y = Phaser.Math.Between(0, 550);
const str = `${Phaser.Math.RND.pick(this.adjectives)} ${Phaser.Math.RND.pick(this.nouns)}`;
const font = { font: '24px Arial' };
const text = group.create(x, y, str, font);
Метод Phaser.Math.Between() задает координаты, а Phaser.Math.RND.pick() случайным образом выбирает элементы из массивов. Созданный объект text автоматически добавляется в группу и отображается на сцене.
Настройка интерактивности для текста
Просто добавить текст недостаточно для взаимодействия с ним. Чтобы объект реагировал на события мыши, ему необходимо назначить интерактивную область (хитбокс). В примере для каждого текстового объекта создается прямоугольная область, соответствующая его размерам.
text.setInteractive(new Phaser.GameObjects.Rectangle(0, 0, text.width, text.height), Phaser.Geom.Rectangle.Contains);
Первый аргумент setInteractive — геометрическая область (в данном случае Phaser.Geom.Rectangle). Второй аргумент — функция проверки попадания (Phaser.Geom.Rectangle.Contains). Это означает, что событие сработает только тогда, когда курсор находится в пределах прямоугольника с размерами текста. Без этой настройки события gameobjectover и gameobjectout не будут работать.
Обработка событий наведения и ухода курсора
Интерактивность оживает через обработку событий ввода. В этом примере используются два события: gameobjectover (курсор наведен на объект) и gameobjectout (курсор ушел с объекта).
this.input.on('gameobjectover', (pointer, gameObject) => {
gameObject.setTint(0xff0000, 0xff0000, 0xffff00, 0xff00ff);
});
При наведении вызывается setTint(), который применяет цветовой оттенок к каждому углу текстового объекта. Цвета задаются в шестнадцатеричном формате (например, 0xff0000 — красный).
this.input.on('gameobjectout', (pointer, gameObject) => {
gameObject.clearTint();
});
При уходе курсора оттенок очищается методом clearTint(), возвращая тексту исходный вид. Эти события срабатывают только для объектов, у которых вызван setInteractive().
Конфигурация и запуск игры
Как и в любом проекте Phaser, необходимо создать конфигурационный объект и инициализировать экземпляр игры. Конфигурация определяет базовые параметры, такие как тип рендерера, размеры холста и главную сцену.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Указание type: Phaser.WEBGL позволяет использовать аппаратное ускорение, что повышает производительность. Поле parent содержит ID HTML-элемента, в который будет встроен canvas игры. Класс Example передается как главная сцена, которая запустится сразу после инициализации движка.
Что попробовать дальше
Вы научились создавать интерактивные текстовые объекты в Phaser, которые реагируют на наведение курсора. Этот механизм — основа для построения отзывчивых интерфейсов и игровых меню. Для экспериментов попробуйте изменить логику генерации текста, добавить анимацию при наведении (например, масштабирование с помощью setScale) или реализовать обработку клика (gameobjectdown) для запуска действий. Также можно подключить физический движок, чтобы текст реагировал на столкновения.
