О чем этот пример
В Phaser при использовании физики Matter.js разработчики часто сталкиваются с неожиданным поведением: изменение свойства `scale` у спрайта с физическим телом не приводит к изменению размеров коллайдера. Это может вызвать проблемы с обнаружением столкновений, когда визуальное представление объекта не соответствует его физической модели. В этой статье мы разберем, почему это происходит, и как правильно изменять размер физических тел в Matter.js.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('blue', 'assets/sprites/columns-blue.png');
}
create ()
{
this.matter.world.setBounds().disableGravity();
// By default it will create a rectangular body the size of the texture
const rect = this.matter.add.image(200, 50, 'blue');
// Just make the body move around and bounce
rect.setVelocity(3, 1);
rect.setAngularVelocity(0.01);
rect.setBounce(1);
rect.setFriction(0, 0, 0);
rect.setInteractive();
rect.once('pointerup', () =>
{
rect.scale = 2;
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#1b1464',
parent: 'phaser-example',
physics: {
default: 'matter',
matter: {
debug: true
}
},
scene: Example
};
const game = new Phaser.Game(config);
Проблема: scale не влияет на физическое тело
В примере мы создаем физический объект с помощью this.matter.add.image. При клике на объект мы пытаемся увеличить его в два раза, устанавливая свойство rect.scale = 2. Визуально спрайт увеличивается, но его физическое тело (коллайдер) остается прежнего размера.
rect.once('pointerup', () =>
{
rect.scale = 2;
});
Это происходит потому, что Matter.js работает с геометрией тел, заданной при их создании. Простое изменение свойства scale игрового объекта обновляет только его отрисовку, но не вносит изменений в движок физики.
Как правильно изменить размер физического тела
Для изменения размеров тела в Matter.js необходимо создать новую геометрию (или масштабировать существующую) и применить ее к телу. Phaser предоставляет для этого метод setBody.
Предположим, мы хотим увеличить тело в два раза. Нам нужно: 1. Получить текущую конфигурацию тела. 2. Создать новое тело с измененными размерами. 3. Заменить старое тело новым.
rect.once('pointerup', () => {
// Меняем визуальный масштаб
rect.scale = 2;
// Получаем текущие размеры оригинального спрайта (до scale)
const width = rect.width;
const height = rect.height;
// Создаем новое тело - прямоугольник с увеличенными размерами
const newBody = this.matter.bodies.rectangle(rect.x, rect.y, width * 2, height * 2);
// Заменяем тело у спрайта
rect.setBody(newBody);
});
Важно: rect.width и rect.height возвращают исходные размеры текстуры, а не масштабированные.
Копирование свойств физического тела
При создании нового тела важно перенести все физические свойства из старого тела, чтобы сохранить поведение объекта. Ключевые свойства, которые стоит скопировать:
rect.once('pointerup', () => {
rect.scale = 2;
const width = rect.width;
const height = rect.height;
// Создаем новое тело
const newBody = this.matter.bodies.rectangle(rect.x, rect.y, width * 2, height * 2);
// Копируем важные свойства из старого тела
const oldBody = rect.body;
newBody.isStatic = oldBody.isStatic;
newBody.restitution = oldBody.restitution; // Аналог setBounce
newBody.friction = oldBody.friction;
newBody.frictionAir = oldBody.frictionAir; // Аналог setFriction(0,0,0)
newBody.inverseInertia = oldBody.inverseInertia;
// Переносим скорость и угловую скорость
newBody.velocity = oldBody.velocity;
newBody.angularVelocity = oldBody.angularVelocity;
rect.setBody(newBody);
});
Этот подход гарантирует, что объект сохранит свою скорость, отскок (setBounce) и другие физические характеристики после изменения размера.
Альтернативный подход: изначальное планирование
Если вам заранее известны возможные размеры объекта, более эффективным решением будет создать несколько тел и переключаться между ними.
create() {
// ... создание rect ...
// Создаем два тела: обычное и увеличенное
const normalBody = this.matter.bodies.rectangle(0, 0, rect.width, rect.height);
const scaledBody = this.matter.bodies.rectangle(0, 0, rect.width * 2, rect.height * 2);
// Сохраняем тела в объекте для повторного использования
rect.normalBody = normalBody;
rect.scaledBody = scaledBody;
rect.once('pointerup', () => {
rect.scale = 2;
// Используем заранее созданное тело
rect.setBody(rect.scaledBody);
});
}
Этот подход особенно полезен, если изменение размера происходит часто, так как избегает постоянного создания новых тел во время выполнения игры.
Что попробовать дальше
Изменение свойства scale у объектов с физикой Matter.js влияет только на их отрисовку, но не на физическое тело. Для корректного изменения размеров коллайдера необходимо создавать новое тело с помощью this.matter.bodies и применять его через setBody. Не забудьте скопировать физические свойства из старого тела, чтобы сохранить поведение объекта. Для экспериментов попробуйте реализовать плавное изменение размера с промежуточными значениями или создать систему, которая автоматически синхронизирует визуальный scale с физическим телом.
