О чем этот пример
В Phaser 3 глобальные плагины обычно регистрируются как синглтоны, но иногда вам может понадобиться несколько независимых экземпляров одного плагина с разным состоянием или конфигурацией. В этой статье мы разберем, как это сделать, используя метод `this.plugins.start()`. Это полезно для создания модульных систем, таких как независимые генераторы контента, менеджеры звука для разных уровней или AI-контроллеры для нескольких NPC с уникальным поведением.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class RandomNamePlugin extends Phaser.Plugins.BasePlugin {
constructor (pluginManager)
{
super('RandomNamePlugin', pluginManager);
this.syllables1 = [ 'fro', 'tir', 'nag', 'bli', 'mon', 'zip' ];
this.syllables2 = [ 'fay', 'shi', 'zag', 'blarg', 'rash', 'izen' ];
this.current = this.syllables1;
}
init ()
{
console.log('Plugin is alive');
}
changeSet ()
{
this.current = this.syllables2;
}
getName ()
{
let name = '';
for (let i = 0; i < Phaser.Math.Between(2, 4); i++)
{
name = name.concat(Phaser.Utils.Array.GetRandom(this.current));
}
return Phaser.Utils.String.UppercaseFirst(name);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
plugins: {
global: [
{ key: 'RandomNamePlugin', plugin: RandomNamePlugin }
]
},
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('elephant', 'assets/sprites/elephant.png');
}
function create ()
{
// Start two instances of the 'RandomNamePlugin' running in the Plugin Manager.
// We will reference them with the keys 'myPluginRef1' and 'myPluginRef2'
let plugin1 = this.plugins.start('RandomNamePlugin', 'myPluginRef1');
let plugin2 = this.plugins.start('RandomNamePlugin', 'myPluginRef1');
// Make the 2nd instance of our plugin use the alternative set of syllables
plugin2.changeSet();
let name1 = plugin1.getName();
let name2 = plugin2.getName();
this.add.image(300, 300, 'elephant');
this.add.image(500, 300, 'elephant');
this.add.text(250, 400, name1, { font: '16px Courier', fill: '#00ff00' });
this.add.text(450, 400, name2, { font: '16px Courier', fill: '#ffff00' });
}
Создание класса плагина
Любой плагин в Phaser 3 должен наследоваться от базового класса Phaser.Plugins.BasePlugin. В конструкторе мы вызываем super(), передавая имя плагина и менеджер плагинов. Здесь же инициализируется начальное состояние плагина.
В нашем примере плагин RandomNamePlugin генерирует случайные имена из двух наборов слогов. Обратите внимание, что набор слогов хранится в свойстве this.current, которое можно изменить.
class RandomNamePlugin extends Phaser.Plugins.BasePlugin {
constructor (pluginManager) {
super('RandomNamePlugin', pluginManager);
this.syllables1 = [ 'fro', 'tir', 'nag', 'bli', 'mon', 'zip' ];
this.syllables2 = [ 'fay', 'shi', 'zag', 'blarg', 'rash', 'izen' ];
this.current = this.syllables1;
}
init () {
console.log('Plugin is alive');
}
changeSet () {
this.current = this.syllables2;
}
getName () {
let name = '';
for (let i = 0; i < Phaser.Math.Between(2, 4); i++) {
name = name.concat(Phaser.Utils.Array.GetRandom(this.current));
}
return Phaser.Utils.String.UppercaseFirst(name);
}
}
Регистрация плагина в конфигурации игры
Чтобы плагин стал доступен в менеджере плагинов игры, его нужно зарегистрировать в глобальном разделе конфигурации plugins. Ключ key — это строка, по которой вы сможете позже создать экземпляр плагина. Важно: на этом этапе плагин только зарегистрирован, но не запущен.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
plugins: {
global: [
{ key: 'RandomNamePlugin', plugin: RandomNamePlugin }
]
},
scene: {
preload: preload,
create: create
}
};
let game = new Phaser.Game(config);
Запуск нескольких экземпляров плагина
Магия происходит в сцене, в методе create. Метод this.plugins.start() делает две вещи: создает новый экземпляр зарегистрированного плагина и возвращает ссылку на него. Второй необязательный параметр — это уникальный ключ для этого экземпляра. Если его не указать, будет использоваться ключ из конфигурации, и вы получите синглтон.
function create ()
{
let plugin1 = this.plugins.start('RandomNamePlugin', 'myPluginRef1');
let plugin2 = this.plugins.start('RandomNamePlugin', 'myPluginRef2');
}
Работа с независимыми состояниями
После создания экземпляры plugin1 и plugin2 полностью независимы. Вызов метода changeSet() у второго экземпляра изменит его внутреннее состояние (this.current), но не затронет первый. Это демонстрирует мощь подхода: каждый экземпляр может иметь свою собственную конфигурацию и данные.
plugin2.changeSet();
let name1 = plugin1.getName(); // Использует syllables1
let name2 = plugin2.getName(); // Использует syllables2
Визуализация результата
Для наглядности сгенерированные имена выводятся на экран с помощью this.add.text(). Разные цвета текста помогают визуально разделить результат работы двух разных экземпляров плагина.
this.add.text(250, 400, name1, { font: '16px Courier', fill: '#00ff00' });
this.add.text(450, 400, name2, { font: '16px Courier', fill: '#ffff00' });
Что попробовать дальше
Использование this.plugins.start() с уникальным ключом позволяет создавать множественные, изолированные экземпляры глобального плагина. Это открывает путь к более чистой и модульной архитектуре. Попробуйте применить этот подход для создания нескольких независимых систем частиц с разными пресетами, отдельных таймеров обратного отсчета для разных игровых событий или уникальных генераторов ландшафта для каждой локации в вашей игре.
