О чем этот пример
Создание визуально впечатляющих взрывов — ключевой элемент в разработке игр. В этом примере мы рассмотрим мощный, но часто упускаемый из виду инструмент Phaser — Render Texture. Используя комбинацию `renderTexture`, `follower` по кривой и управляемого твина, мы создадим эффект ядерного гриба, который рисуется динамически и может быть вызван в любой точке экрана. Этот подход позволяет генерировать уникальные, не повторяющиеся эффекты без использования большого количества спрайтов или тяжелых систем частиц, что отлично подходит для мобильных игр и проектов с ограниченными ресурсами.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
nukeFX;
blast;
rt;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('fire', 'assets/particles/muzzleflash3.png');
this.load.image('smoke', 'assets/particles/smoke-puff.png');
}
create ()
{
this.rt = this.make.renderTexture({ x: 0, y: 0, width: 800, height: 600 });
this.blast = this.add.follower(null, 50, 350, 'smoke');
const curve = new Phaser.Curves.Spline([ 200, 500, 600, 500, 625, 475, 200, 500, 400, 500, 400, 250 ]);
this.blast.setPath(curve);
this.nukeFX = this.tweens.add({
targets: this.blast,
scaleX: 8,
scaleY: 8,
alpha: 0,
duration: 1500,
ease: 'Bounce.easeInOut',
complete: function ()
{
console.log('Complete');
this.rt.clear().render(); this.blast.alpha = 0;
},
paused: true
});
this.nukeFX.pause();
this.nukeFX.setCallback('update', this.draw, [], this);
this.input.on('pointerdown', pointer =>
{
this.detonate(pointer.x, pointer.y);
}, this);
}
detonate (x, y)
{
this.blast.setPosition(x, y).setScale(1).setAlpha(1);
this.blast.startFollow(200);
this.nukeFX.restart();
}
draw ()
{
this.blast.setRotation(Math.random() * 4 - 2);
this.blast.setTexture(Math.random() < 0.8 ? 'fire' : 'smoke');
this.rt.draw(this.blast).render();
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example,
width: 800,
height: 600
};
const game = new Phaser.Game(config);
Создание Render Texture и объекта Blast
В методе create мы инициализируем основные компоненты эффекта.
Сначала создается renderTexture размером с игровое окно (800x600). Это наш холст, на котором будет отрисовываться каждый кадр взрыва.
Затем создается объект this.blast типа follower. Изначально ему не задана текстура (первый аргумент null), но он будет следовать по заданной кривой. Кривая создается с помощью Phaser.Curves.Spline, которая определяет сложную траекторию движения эффекта.
create ()
{
this.rt = this.make.renderTexture({ x: 0, y: 0, width: 800, height: 600 });
this.blast = this.add.follower(null, 50, 350, 'smoke');
const curve = new Phaser.Curves.Spline([ 200, 500, 600, 500, 625, 475, 200, 500, 400, 500, 400, 250 ]);
this.blast.setPath(curve);
// ...
}
Настройка анимации взрыва
Сердце эффекта — твин, который управляет масштабом, прозрачностью и вызывает функцию отрисовки.
Мы создаем твин с помощью this.tweens.add, который будет анимировать this.blast. Он увеличивает масштаб до 8 раз, уменьшает прозрачность до 0 за 1.5 секунды и использует эффект упругости 'Bounce.easeInOut'.
Ключевые моменты:
1. Твин изначально поставлен на паузу (paused: true).
2. В коллбэке complete мы очищаем renderTexture (this.rt.clear().render()) и скрываем объект blast.
3. С помощью setCallback мы привязываем функцию draw к событию 'update' твина. Это значит, что draw будет вызываться на каждом кадре анимации.
this.nukeFX = this.tweens.add({
targets: this.blast,
scaleX: 8,
scaleY: 8,
alpha: 0,
duration: 1500,
ease: 'Bounce.easeInOut',
complete: function ()
{
console.log('Complete');
this.rt.clear().render(); this.blast.alpha = 0;
},
paused: true
});
this.nukeFX.pause();
this.nukeFX.setCallback('update', this.draw, [], this);
Запуск взрыва и функция отрисовки
Запуск эффекта происходит по клику мыши. Обработчик события 'pointerdown' вызывает метод detonate, передавая координаты клика.
Метод detonate сбрасывает состояние объекта blast в начальную точку, запускает его движение по кривой (startFollow) и перезапускает анимацию твина (restart).
this.input.on('pointerdown', pointer =>
{
this.detonate(pointer.x, pointer.y);
}, this);
// ...
detonate (x, y)
{
this.blast.setPosition(x, y).setScale(1).setAlpha(1);
this.blast.startFollow(200);
this.nukeFX.restart();
}
Функция draw — это то, что создает "шум" и разнообразие во взрыве. На каждом кадре твина она:
1. Задает случайный угол поворота объекту blast.
2. С вероятностью 80% меняет его текстуру на 'fire', и на 20% — на 'smoke'.
3. Отрисовывает текущее состояние blast на renderTexture и немедленно применяет рендер.
draw ()
{
this.blast.setRotation(Math.random() * 4 - 2);
this.blast.setTexture(Math.random() < 0.8 ? 'fire' : 'smoke');
this.rt.draw(this.blast).render();
}
Инициализация игры
Стандартная конфигурация игры Phaser, которая указывает на наш класс сцены Example и задает размер окна 800x600 пикселей.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example,
width: 800,
height: 600
};
const game = new Phaser.Game(config);
Что попробовать дальше
Использование renderTexture для создания эффектов — это мощный метод, который сочетает производительность и гибкость. Вы не ограничены спрайтами частиц и можете создавать сложные, накапливающиеся визуалы. Для экспериментов попробуйте: изменить форму кривой пути взрыва, добавить больше текстур для частиц, изменить логику выбора текстур в draw, использовать renderTexture для создания следов или долгосрочных эффектов на карте, а также комбинировать этот подход с системой частиц Phaser для еще более сложных взрывов.
