О чем этот пример
Разработка игр часто требует повторного использования однотипных объектов с уникальным поведением. Копирование кода для каждого врага или бонуса приводит к беспорядку. В этой статье мы рассмотрим, как создать собственный класс игрового объекта, унаследовав его от `Phaser.GameObjects.Sprite`, и зарегистрировать фабричный метод для удобного создания через `this.add`. Этот подход делает код чище, логику — изолированной, а объекты — легко переиспользуемыми.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class EnemyRobot extends Phaser.GameObjects.Sprite {
constructor (scene, x, y)
{
super(scene, x, y);
this.setTexture('contra');
this.setScale(2);
}
preUpdate (time, delta)
{
super.preUpdate(time, delta);
this.rotation += 0.01;
}
}
let config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
pixelArt: true,
scene: {
init: init,
preload: preload,
create: create
}
};
let game = new Phaser.Game(config);
function init ()
{
Phaser.GameObjects.GameObjectFactory.register('robot', function (x, y)
{
let sprite = new EnemyRobot(this.scene, x, y);
this.displayList.add(sprite);
this.updateList.add(sprite);
return sprite;
});
}
function preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('contra', 'assets/pics/contra3.png');
}
function create ()
{
this.add.robot(200, 200);
this.add.robot(400, 300);
this.add.robot(600, 400);
}
Наследование от базового класса Sprite
В Phaser все визуальные объекты наследуются от Phaser.GameObjects.GameObject. Для спрайтов базовым классом является Phaser.GameObjects.Sprite. Создав свой класс, мы можем инкапсулировать его внешний вид и поведение.
Ключевые моменты конструктора:
- Вызов super(scene, x, y) обязателен для инициализации родительского класса.
- Методы setTexture и setScale задают изображение и масштаб объекта.
class EnemyRobot extends Phaser.GameObjects.Sprite {
constructor (scene, x, y) {
super(scene, x, y);
this.setTexture('contra');
this.setScale(2);
}
}
Добавление пользовательской логики в preUpdate
Для добавления кастомного поведения, которое должно выполняться каждый кадр, используется метод preUpdate. В нашем примере объект вращается.
Важно всегда вызывать super.preUpdate(time, delta), чтобы родительский класс мог выполнить свою внутреннюю логику обновления.
preUpdate (time, delta) {
super.preUpdate(time, delta);
this.rotation += 0.01;
}
Регистрация фабричного метода
Чтобы создавать наш кастомный объект так же удобно, как стандартные (this.add.image), нужно расширить GameObjectFactory. Это делается в функции init сцены.
Метод register принимает ключ (имя метода, например, 'robot') и фабричную функцию. Внутри функции создаётся экземпляр нашего класса, добавляется в списки отображения и обновления сцены и возвращается.
function init () {
Phaser.GameObjects.GameObjectFactory.register('robot', function (x, y) {
let sprite = new EnemyRobot(this.scene, x, y);
this.displayList.add(sprite);
this.updateList.add(sprite);
return sprite;
});
}
Использование кастомного объекта
После регистрации метод this.add.robot становится доступен в сцене. Создание экземпляров ничем не отличается от работы со встроенными типами объектов.
function create () {
this.add.robot(200, 200);
this.add.robot(400, 300);
this.add.robot(600, 400);
}
Каждый созданный робот будет автоматически обновляться (preUpdate) и вращаться благодаря логике, описанной в классе.
Что попробовать дальше
Создание собственных классов игровых объектов — мощный паттерн в Phaser для структурирования кода. Вы можете экспериментировать: добавить в класс EnemyRobot физическое тело через this.scene.physics.add.existing(this), реализовать ИИ для патрулирования или создать иерархию классов для разных типов врагов (летающий робот, стреляющий робот), наследуя их от базового EnemyRobot.
