О чем этот пример
Использование стандартных классов Phaser, таких как `Image` или `Sprite`, — это быстрый способ добавить графику в игру. Но что делать, если вам нужен объект с предопределённым поведением, внешним видом или логикой? В этом случае на помощь приходит наследование и создание собственных классов игровых объектов. В этой статье мы разберём, как создать кастомный класс на основе `Phaser.GameObjects.Image`, чтобы инкапсулировать настройки и упростить создание однотипных объектов в сцене.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('contra', 'assets/pics/contra3.png');
}
create ()
{
this.children.add(new EnemyRobot(this, 264, 250));
this.children.add(new EnemyRobot(this, 464, 350));
this.children.add(new EnemyRobot(this, 664, 450));
}
}
class EnemyRobot extends Phaser.GameObjects.Image
{
constructor (scene, x, y)
{
super(scene);
this.setTexture('contra');
this.setPosition(x, y);
this.setScale(2);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Зачем создавать свои классы объектов?
Представьте, что в вашей игре есть враг-робот, который должен всегда появляться с определённой текстурой, увеличенным размером и, возможно, заранее заданной анимацией или физическим телом. Каждый раз при создании такого робота вам пришлось бы повторять одни и те же вызовы API: setTexture, setScale, setPosition и т.д. Это ведёт к дублированию кода и усложняет его поддержку.
Создание собственного класса EnemyRobot решает эту проблему. Вы описываете конфигурацию объекта один раз в его конструкторе, а затем можете создавать экземпляры одной строкой. Это делает код сцены чище, а логику врага проще развивать — вся информация о нём сосредоточена в одном месте.
Анализ исходного кода примера
Пример состоит из двух ключевых частей: сцены Example и пользовательского класса EnemyRobot. Давайте посмотрим на сцену:
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('contra', 'assets/pics/contra3.png');
}
create ()
{
this.children.add(new EnemyRobot(this, 264, 250));
this.children.add(new EnemyRobot(this, 464, 350));
this.children.add(new EnemyRobot(this, 664, 450));
}
}
В методе preload задаётся базовый URL для загрузки и загружается текстура с ключом 'contra'. В методе create происходит самое интересное: создаются три экземпляра нашего кастомного робота. Обратите внимание, что для добавления объекта в систему отображения Phaser используется this.children.add(). Это стандартный способ регистрации любого игрового объекта в сцене. Конструктору EnemyRobot передаются ссылка на саму сцену (this) и координаты.
Создание класса EnemyRobot
Класс EnemyRobot наследуется от стандартного Phaser.GameObjects.Image. Это даёт ему все свойства и методы изображения.
class EnemyRobot extends Phaser.GameObjects.Image
{
constructor (scene, x, y)
{
super(scene);
this.setTexture('contra');
this.setPosition(x, y);
this.setScale(2);
}
}
Разберём конструктор по шагам:
1. `super(scene);` — Вызов конструктора родительского класса (`Image`) является обязательным. Он инициализирует объект внутри фреймворка и привязывает его к переданной сцене.
2. `this.setTexture('contra');` — Устанавливает текстуру, которая была загружена в сцене под ключом `'contra'`. Это внутренний спрайт из примера.
3. `this.setPosition(x, y);` — Задаёт позицию объекта на игровом поле. Координаты `x` и `y` передаются из вызова в сцене.
4. `this.setScale(2);` — Увеличивает спрайт в 2 раза. Это демонстрация того, как можно сразу применять любые стандартные трансформации к объекту.
После выполнения конструктора объект полностью настроен и готов к отображению.
Конфигурация и запуск игры
Завершающая часть примера — стандартная конфигурация движка Phaser и создание экземпляра игры.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Конфиг указывает движку использовать WebGL или Canvas автоматически (Phaser.AUTO), монтировать игру в HTML-элемент с id='phaser-example' и использовать нашу сцену Example в качестве начальной. Инициализация new Phaser.Game(config) запускает весь игровой цикл.
Что попробовать дальше
Создание собственных классов игровых объектов через наследование — мощный паттерн в Phaser для структурирования кода и повторного использования логики. Вы инкапсулируете настройки и поведение объекта в одном месте, что соответствует принципам ООП.
**Идеи для экспериментов:**
1. Добавьте в конструктор EnemyRobot вызов this.setFlipX(true) или this.setAngle(45), чтобы роботы появлялись отражёнными или повёрнутыми.
2. Реализуйте в классе методы preUpdate() для добавления простой анимации, например, покачивания (this.y += Math.sin(time) * 0.5).
3. Расширьте класс, добавив физическое тело через this.scene.physics.add.existing(this) в конструкторе, чтобы роботы могли сталкиваться с миром.
