О чем этот пример
Частая стрельба в играх создаёт нагрузку: постоянное создание и удаление спрайтов тормозит производительность. Решение — пул объектов (Object Pool), который переиспользует заранее созданные экземпляры. В этой статье разберём пример управления пулей в Phaser: от создания пула до оптимизированной стрельбы. Вы научитесь создавать эффективные системы, которые не нагружают память и работают плавно даже на слабых устройствах.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
lastFired = 0;
cursors;
stats;
speed;
ship;
bullets;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('ship', 'assets/sprites/ship.png');
this.load.image('bullet', 'assets/sprites/bullet.png');
}
create ()
{
class Bullet extends Phaser.GameObjects.Image
{
constructor (scene)
{
super(scene, 0, 0, 'bullet');
this.speed = Phaser.Math.GetSpeed(400, 1);
}
fire (x, y)
{
this.setPosition(x, y - 50);
this.setActive(true);
this.setVisible(true);
}
update (time, delta)
{
this.y -= this.speed * delta;
if (this.y < -50)
{
this.setActive(false);
this.setVisible(false);
}
}
}
this.bullets = this.add.group({
classType: Bullet,
maxSize: 10,
runChildUpdate: true
});
this.ship = this.add.sprite(400, 500, 'ship').setDepth(1);
this.cursors = this.input.keyboard.createCursorKeys();
this.speed = Phaser.Math.GetSpeed(300, 1);
}
update (time, delta)
{
if (this.cursors.left.isDown)
{
this.ship.x -= this.speed * delta;
}
else if (this.cursors.right.isDown)
{
this.ship.x += this.speed * delta;
}
if (this.cursors.up.isDown && time > this.lastFired)
{
const bullet = this.bullets.get();
if (bullet)
{
bullet.fire(this.ship.x, this.ship.y);
this.lastFired = time + 50;
}
}
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Зачем нужен пул объектов?
Вместо того чтобы создавать новый спрайт при каждом выстреле и удалять его при выходе за границы экрана, мы заранее создаём группу объектов (this.bullets). Когда пуля нужна — мы берём её из пула и активируем. Когда она покидает экран — деактивируем и возвращаем в пул для повторного использования.
Это экономит ресурсы процессора и памяти, особенно важно для мобильных игр или проектов с сотнями объектов на сцене.
this.bullets = this.add.group({
classType: Bullet,
maxSize: 10,
runChildUpdate: true
});
Создаём класс пули
Класс Bullet наследуется от Phaser.GameObjects.Image. В конструкторе задаётся текстура и рассчитывается скорость.
Метод fire() помещает пулю в нужную позицию и делает её активной и видимой.
Метод update() двигает пулю вверх и деактивирует, если она улетела за границу экрана.
class Bullet extends Phaser.GameObjects.Image
{
constructor (scene)
{
super(scene, 0, 0, 'bullet');
this.speed = Phaser.Math.GetSpeed(400, 1);
}
fire (x, y)
{
this.setPosition(x, y - 50);
this.setActive(true);
this.setVisible(true);
}
update (time, delta)
{
this.y -= this.speed * delta;
if (this.y < -50)
{
this.setActive(false);
this.setVisible(false);
}
}
}
Управление кораблём и стрельба
В основном цикле update() обрабатывается ввод с клавиатуры. Корабль двигается влево-вправо с фиксированной скоростью, рассчитанной через Phaser.Math.GetSpeed.
Стрельба происходит при зажатии клавиши UP. Чтобы избежать слишком частых выстрелов, используется задержка на основе времени (this.lastFired).
if (this.cursors.up.isDown && time > this.lastFired)
{
const bullet = this.bullets.get();
if (bullet)
{
bullet.fire(this.ship.x, this.ship.y);
this.lastFired = time + 50;
}
}
Как работает `this.bullets.get()`
Метод get() группы объектов ищет первый неактивный (active=false) объект в пуле. Если такой найден — он возвращается для повторного использования. Если все объекты активны (пул переполнен), метод вернёт null, и выстрел не произойдёт.
Параметр maxSize: 10 ограничивает максимальное количество пуль, предотвращая бесконечный рост.
const bullet = this.bullets.get(); // Берём пулю из пула
if (bullet) // Проверяем, что пуля доступна
{
bullet.fire(this.ship.x, this.ship.y); // Активируем её
}
Что попробовать дальше
Пул объектов — мощный паттерн для оптимизации игр с большим количеством однотипных объектов. Вы можете адаптировать этот подход для частиц, врагов, снарядов.
Экспериментируйте:
- Увеличьте maxSize и добавьте автоматическую стрельбу.
- Измените логику update() пули, чтобы она летела по дуге или преследовала цель.
- Добавьте разные типы пуль в один пул, используя classType.
