О чем этот пример
Создание сложных сцен с множеством анимаций — обычное дело в разработке игр. Но что, если вам нужно остановить или удалить часть этих анимаций, не затрагивая остальные? В Phaser есть мощный инструмент для точечного управления твинами. В этой статье мы разберем пример, который демонстрирует, как массово останавливать анимации у произвольного набора игровых объектов одним вызовом. Этот подход полезен для создания интерактивных сцен, кат-сцен или очистки ресурсов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('block', 'assets/sprites/50x50.png');
this.load.spritesheet('fish', 'assets/sprites/fish-136x80.png', { frameWidth: 136, frameHeight: 80 });
}
function create ()
{
var c = 0;
var blocks = [];
for (var i = 0; i < 108; i++)
{
var block = this.add.image(0, 0, 'block').setScale(0.3);
blocks.push(block);
this.tweens.add({
targets: block,
scaleX: 1,
scaleY: 1,
ease: 'Sine.easeInOut',
duration: 300,
delay: c * 50,
repeat: -1,
yoyo: true
});
c++;
if (c % 12 === 0)
{
c = 0;
}
}
Phaser.Actions.GridAlign(blocks, {
width: 12,
height: 10,
cellWidth: 60,
cellHeight: 60,
x: 70,
y: 60
});
var image1 = this.add.image(0, 80, 'fish', 0);
this.tweens.add({
targets: image1,
props: {
x: { value: 700, duration: 4000, flipX: true },
y: { value: 500, duration: 8000, },
},
ease: 'Sine.easeInOut',
yoyo: true,
repeat: -1
});
var image2 = this.add.image(400, 80, 'fish', 1);
this.tweens.add({
targets: image2,
props: {
x: { value: 500, duration: 2000, flipX: true },
y: { value: 500, duration: 10000, },
},
ease: 'Sine.easeInOut',
yoyo: true,
repeat: -1
});
var image3 = this.add.image(800, 200, 'fish', 2).setFlipX(true);
this.tweens.add({
targets: image3,
props: {
x: { value: 70, flipX: true },
y: { value: 250 },
},
duration: 3000,
ease: 'Power1',
yoyo: true,
repeat: -1
});
var image4 = this.add.image(100, 550, 'fish', 2).setScale(0.75);
this.tweens.add({
targets: image4,
props: {
x: { value: 700, duration: 2000, flipX: true },
y: { value: 50, duration: 15000, },
},
ease: 'Sine.easeInOut',
yoyo: true,
repeat: -1
});
this.input.once('pointerdown', () => {
this.tweens.killTweensOf([ image1, blocks, image4 ]);
});
}
Подготовка сцены и создание множества анимированных объектов
В начале кода создается стандартная конфигурация игры Phaser и загружаются спрайты. Основная логика находится в функции create. Первым делом создается массив из 108 блоков (blocks). Каждому блоку назначается персональный твин, который циклически меняет его масштаб от маленького к большому и обратно.
Задержка (delay) для каждого твина рассчитывается так, чтобы анимации запускались волнами, создавая эффект пульсации.
this.tweens.add({
targets: block,
scaleX: 1,
scaleY: 1,
ease: 'Sine.easeInOut',
duration: 300,
delay: c * 50,
repeat: -1,
yoyo: true
});
После создания всех блоков они выравниваются в сетку с помощью Phaser.Actions.GridAlign. Это чисто визуальное преобразование, не влияющее на твины.
Создание независимых анимированных спрайтов
Далее в сцене создаются четыре рыбы (image1, image2, image3, image4). Каждая из них получает свой собственный, более сложный твин. Эти твины анимируют несколько свойств одновременно (координаты `xиy), имеют разную длительность (duration) и используют эффект отражения спрайта (flipX`).
Ключевой момент: каждый твин привязан к своему целевому объекту (targets), но управляется централизованно через менеджер твинов сцены this.tweens.
this.tweens.add({
targets: image1,
props: {
x: { value: 700, duration: 4000, flipX: true },
y: { value: 500, duration: 8000, },
},
ease: 'Sine.easeInOut',
yoyo: true,
repeat: -1
});
Массовая остановка анимаций по событию
Вся магия происходит в обработчике клика (pointerdown). По одному клику мыши нужно остановить анимации у первой рыбы (image1), у всех блоков в массиве (blocks) и у четвертой рыбы (image4). При этом вторая и третья рыбы должны продолжать двигаться.
Для этого используется метод this.tweens.killTweensOf(). Он принимает один целевой объект или массив объектов и немедленно останавливает все твины, которые в данный момент воздействуют на эти объекты.
this.input.once('pointerdown', () => {
this.tweens.killTweensOf([ image1, blocks, image4 ]);
});
Важно понимать: в метод передается не сам массив твинов, а массив игровых объектов (Image), которые являются целями этих твинов. Система Phaser сама находит и завершает все связанные с ними активные анимации. Объекты image2 и image3 не входят в список, поэтому их твины продолжают работать.
Почему это работает: связь твинов и целей
Метод killTweensOf работает, потому что менеджер твинов this.tweens внутренне отслеживает связь между каждым созданным твином и его свойством targets. Когда вы вызываете этот метод, система проходит по всем активным твинам и проверяет, ссылаются ли их цели на переданные объекты. Если ссылка найдена — твин немедленно уничтожается.
Это мощный и безопасный способ управления анимациями. Вам не нужно хранить ссылки на каждый созданный твин в отдельной переменной, что особенно удобно при работе с десятками или сотнями анимированных объектов, как в нашем примере с блоками.
Что попробовать дальше
Использование this.tweens.killTweensOf() позволяет гибко и эффективно управлять жизненным циклом анимаций в Phaser. Вы можете останавливать анимации у любых комбинаций объектов по событию таймера, коллизии или пользовательского ввода. Для экспериментов попробуйте
- Останавливать твины не по клику, а при столкновении рыб
- Реализовать кнопку 'Пауза', которая останавливает все твины на сцене через
this.tweens.killTweensOf(this.children.list) - Восстанавливать остановленные анимации с помощью методов
resumeили перезапуская твины
