О чем этот пример
В стандартном режиме движок Arcade Physics обновляется автоматически на каждом кадре. Но бывают случаи, когда логика игры требует нестандартной синхронизации или ручного контроля. Пример демонстрирует, как включить кастомное обновление физики (customUpdate) и вручную запускать расчёты столкновений и движения. Это полезно для создания пошаговых игр, синхронизации с внешними сервисами или тонкой настройки производительности.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super({
physics: {
arcade: {
customUpdate: true,
debug: true,
fixedStep: false,
fps: 60,
gravity: { y: 200 }
}
}
});
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('block', 'assets/sprites/block.png');
this.load.image('logo', 'assets/sprites/phaser3-logo.png');
this.load.image('red', 'assets/particles/red.png');
this.load.image('sky', 'assets/skies/space2.png');
}
create ()
{
this.add.image(400, 300, 'sky');
const group = this.physics.add.group({
bounceX: 1,
bounceY: 1,
collideWorldBounds: true
});
group.create(100, 200, 'block').setVelocity(100, 200);
group.create(500, 200, 'block').setVelocity(-100, -100);
group.create(300, 400, 'block').setVelocity(60, 100);
group.create(600, 300, 'block').setVelocity(-30, -50);
this.physics.add.collider(group);
const particles = this.add.particles(0, 0, 'red', {
speed: 100,
scale: { start: 1, end: 0 },
blendMode: 'ADD'
});
const logo = this.physics.add.image(400, 100, 'logo');
logo.setVelocity(100, 200);
logo.setBounce(1, 1);
logo.setCollideWorldBounds(true);
particles.startFollow(logo);
const gui = new dat.GUI({ width: 400 });
gui.add(this.physics, 'enableUpdate');
gui.add(this.physics, 'disableUpdate');
const { world } = this.physics;
gui.add({ update: () => { world.update(0, world._frameTimeMS); } }, 'update');
}
}
const gameConfig = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(gameConfig);
Включение кастомного обновления
Ключевой параметр customUpdate: true настраивается в конструкторе сцены внутри конфигурации физики. Это отключает встроенный цикл обновления Arcade Physics.
constructor ()
{
super({
physics: {
arcade: {
customUpdate: true, // Отключаем автообновление физики
debug: true,
fixedStep: false,
fps: 60,
gravity: { y: 200 }
}
}
});
}
С этого момента расчёты скорости, гравитации и столкновений не будут происходить сами по себе. Вам нужно явно вызывать метод world.update().
Создание физических тел и группы
В методе create() мы создаём обычные физические объекты, но они не будут двигаться, пока физика не обновится. Группа с collideWorldBounds и разными скоростями готовится к работе.
const group = this.physics.add.group({
bounceX: 1,
bounceY: 1,
collideWorldBounds: true
});
group.create(100, 200, 'block').setVelocity(100, 200);
group.create(500, 200, 'block').setVelocity(-100, -100);
Коллайдер для группы создаётся стандартно, но проверка столкновений также зависит от ручного вызова update().
this.physics.add.collider(group);
Ручное управление обновлением через GUI
Для демонстрации в интерфейс добавлены три кнопки, управляющие обновлением. Они работают с публичными методами и свойствами движка.
const gui = new dat.GUI({ width: 400 });
gui.add(this.physics, 'enableUpdate');
gui.add(this.physics, 'disableUpdate');
Методы enableUpdate и disableUpdate включают и выключают стандартный автоматический цикл обновления физики, но только если customUpdate равно false. В нашем примере они не будут иметь видимого эффекта, так как автообновление уже отключено на уровне конфигурации.
Самая важная кнопка вызывает world.update() вручную, передавая время, прошедшее с последнего кадра.
const { world } = this.physics;
gui.add({ update: () => { world.update(0, world._frameTimeMS); } }, 'update');
Параметры world.update(0, world._frameTimeMS) означают: первый аргумент (delta) здесь установлен в 0, а второй (time) использует внутреннее время кадра движка. Каждый клик по этой кнопке продвинет физическую симуляцию вперёд на один шаг.
Практическое применение и нюансы
Ручное обновление даёт полный контроль. Вы можете, например, обновлять физику не каждый кадр, а раз в несколько секунд для пошаговой стратегии, или синхронизировать её с анимациями, управляемыми внешней библиотекой.
Важно помнить:
* При customUpdate: true вы полностью отвечаете за вызов world.update().
* Внутреннее свойство world._frameTimeMS хранит время последнего кадра. Для нестандартных интервалов можно передавать своё значение дельты времени.
* Все физические тела, созданные через this.physics.add, зависят от этого обновления.
Без вызовов update() тела будут висеть в воздухе, не реагируя на гравитацию, скорость и столкновения, даже если они заданы.
Что попробовать дальше
Режим customUpdate — мощный инструмент для нестандартных игровых механик, где автоматический цикл обновления не подходит. Вы получаете рычаги для управления производительностью и временем в симуляции. Поэкспериментируйте: привяжите вызов world.update() к событию мыши, таймеру или сетевому пакету. Попробуйте создать пошаговый физический паззл или синхронизировать движение объектов с аудиодорожкой.
