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

Встроенные игровые объекты Phaser — мощный инструмент, но иногда вам нужен уникальный объект с предустановленными свойствами, например, спрайт с фиксированным масштабом или особым поведением. Создание собственного типа игрового объекта через плагин позволяет инкапсулировать эту логику и добавлять новые экземпляры одной строкой, используя знакомый синтаксис `this.add`. Это повышает переиспользование кода и делает его чище. В этой статье мы разберём практический пример создания плагина, который регистрирует новый тип объекта 'clown' — увеличенный спрайт клоуна. Вы научитесь расширять `Phaser.GameObjects.Image`, создавать плагин для его регистрации и интегрировать его в конфигурацию игры.

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

Живой запуск

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

Исходный код


class ClownGameObject extends Phaser.GameObjects.Image {

    constructor (scene, x, y)
    {
        super(scene, x, y, 'clown');

        this.setScale(4);
    }

}

class ClownPlugin extends Phaser.Plugins.BasePlugin {

    constructor (pluginManager)
    {
        super(pluginManager);

        //  Register our new Game Object type
        pluginManager.registerGameObject('clown', this.createClown);
    }

    createClown (x, y)
    {
        return this.displayList.add(new ClownGameObject(this.scene, x, y));
    }

}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    pixelArt: true,
    plugins: {
        global: [
            { key: 'ClownPlugin', plugin: ClownPlugin, start: true }
        ]
    },
    scene: {
        preload: preload,
        create: create
    }
};

let game = new Phaser.Game(config);

function preload ()
{
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('clown', 'assets/sprites/clown.png');
}

function create ()
{
    this.add.clown(400, 200);
    this.add.clown(300, 300);
    this.add.clown(500, 300);
}

Наследуемся от стандартного игрового объекта

Первым шагом создаём класс кастомного игрового объекта. Мы наследуемся от Phaser.GameObjects.Image, что даёт нам все базовые свойства и методы спрайта.

В конструкторе вызываем super, передавая сцену, координаты и ключ текстуры. После этого мы можем сразу задать специфичные для нашего объекта свойства. В примере это фиксированный масштаб.

class ClownGameObject extends Phaser.GameObjects.Image {
    constructor (scene, x, y) {
        super(scene, x, y, 'clown');
        this.setScale(4);
    }
}

Таким образом, каждый созданный объект ClownGameObject будет спрайтом с текстурами 'clown', автоматически увеличенным в 4 раза. Вся эта настройка скрыта внутри класса.

Создаём и регистрируем плагин

Чтобы Phaser узнал о нашем новом типе объекта и позволил создавать его через this.add, нужен плагин. Класс плагина наследуется от Phaser.Plugins.BasePlugin.

Ключевой момент — регистрация в методе конструктора. Мы обращаемся к pluginManager и вызываем метод registerGameObject. Первый аргумент — строка, которая станет именем метода фабрики (в нашем случае 'clown'), второй — функция, которая будет этот объект создавать и возвращать.

class ClownPlugin extends Phaser.Plugins.BasePlugin {
    constructor (pluginManager) {
        super(pluginManager);
        pluginManager.registerGameObject('clown', this.createClown);
    }
    createClown (x, y) {
        return this.displayList.add(new ClownGameObject(this.scene, x, y));
    }
}

Функция createClown создаёт экземпляр нашего ClownGameObject и добавляет его в displayList текущей сцены, что делает объект видимым. Обратите внимание на контекст: this внутри createClown будет ссылаться на экземпляр GameObjectFactory сцены, поэтому у нас есть доступ к this.scene и this.displayList.

Интеграция плагина в конфиг игры

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

В поле plugins.global передаём массив объектов. Каждый объект должен содержать ключ key (уникальное строковое имя), plugin (ссылку на класс плагина) и start: true для автоматического запуска.

const config = {
    // ... другие настройки (тип, размеры)
    plugins: {
        global: [
            { key: 'ClownPlugin', plugin: ClownPlugin, start: true }
        ]
    },
    scene: {
        preload: preload,
        create: create
    }
};

После такой настройки плагин зарегистрирует метод clown в фабрике игровых объектов (this.add) для каждой сцены.

Использование кастомного объекта в сцене

Теперь в коде сцены мы можем создавать наших клоунов так же просто, как и любые стандартные объекты. Phaser автоматически добавит метод clown в объект this.add.

В функции preload загружаем необходимую текстуру.

function preload () {
    this.load.image('clown', 'assets/sprites/clown.png');
}

В функции create вызываем новый метод. Каждый вызов создаёт новый экземпляр ClownGameObject с заданными координатами и предустановленным масштабом.

function create () {
    this.add.clown(400, 200);
    this.add.clown(300, 300);
    this.add.clown(500, 300);
}

Код сцены становится очень чистым и декларативным: мы просто говорим «добавь клоуна в точку (X, Y)».

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

Создание кастомных игровых объектов через плагины — отличный способ структурировать код в больших проектах. Вы инкапсулируете логику создания и настройки объектов, делая её переиспользуемой и скрытой от основного кода сцены. Для экспериментов попробуйте: добавить в класс ClownGameObject физическое тело через this.scene.physics.add.existing(this); реализовать в классе анимацию или кастомный метод update; создать плагин для более сложного составного объекта, используя Phaser.GameObjects.Container в качестве базового класса.