О чем этот пример
При создании игр с физикой часто требуется сделать часть объектов интерактивными для игрока, а другую часть — статичной или управляемой иначе. В примере Phaser с Matter.js показан элегантный способ фильтрации объектов, которые можно перетаскивать мышью, используя группы столкновений (collision groups). Этот подход полезен для создания сложных физических сцен, где нужно точно контролировать взаимодействие игрока с окружением, например, в головоломках или симуляторах.
Версия 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('block', 'assets/sprites/block.png');
this.load.image('mushroom', 'assets/sprites/mushroom2.png');
}
create ()
{
this.matter.world.setBounds();
// Add a few blocks, you can drag all of these with the pointer
const canDrag = this.matter.world.nextGroup();
this.matter.add.image(100, 100, 'block', null, { chamfer: 16 }).setBounce(0.9).setCollisionGroup(canDrag);
this.matter.add.image(300, 100, 'block', null, { chamfer: 16 }).setBounce(0.9).setCollisionGroup(canDrag);
this.matter.add.image(600, 100, 'block', null, { chamfer: 16 }).setBounce(0.9).setCollisionGroup(canDrag);
// Add some mushrooms, you cannot drag these
const noDrag = this.matter.world.nextGroup();
this.matter.add.image(200, 100, 'mushroom', null, { chamfer: 16 }).setBounce(0.9).setCollisionGroup(noDrag);
this.matter.add.image(400, 100, 'mushroom', null, { chamfer: 16 }).setBounce(0.9).setCollisionGroup(noDrag);
this.matter.add.image(500, 100, 'mushroom', null, { chamfer: 16 }).setBounce(0.9).setCollisionGroup(noDrag);
// Our constraint
this.matter.add.mouseSpring({ length: 1, stiffness: 0.6, collisionFilter: { group: canDrag } });
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#1b1464',
parent: 'phaser-example',
physics: {
default: 'matter'
},
scene: Example
};
const game = new Phaser.Game(config);
Основа примера: Группы столкновений Matter.js
В Matter.js, физическом движке, который используется в Phaser, collisionFilter — это объект, определяющий, как тела взаимодействуют друг с другом. Свойство group внутри фильтра позволяет объединять тела в группы. Тела из одной группы по умолчанию не сталкиваются друг с другом (если не задано иное), но, что важно для нашего примера, это свойство также используется для фильтрации при взаимодействии с инструментами вроде мышиной пружины.
Ключевой метод для создания новой уникальной группы — this.matter.world.nextGroup(). Каждый вызов возвращает новое числовое значение, идентифицирующее группу.
Создание перетаскиваемых и статичных объектов
В методе create() сначала создаются объекты, которые мы хотим сделать перетаскиваемыми. Для них создается общая группа canDrag. Каждый блок создается через this.matter.add.image с последующей настройкой.
const canDrag = this.matter.world.nextGroup();
this.matter.add.image(100, 100, 'block', null, { chamfer: 16 }).setBounce(0.9).setCollisionGroup(canDrag);
Здесь { chamfer: 16 } задает скругление углов физического тела для блока. Метод .setCollisionGroup(canDrag) назначает созданное тело в группу canDrag.
Аналогично создается группа noDrag для объектов-грибов, которые не должны реагировать на перетаскивание. Им также назначается своя группа столкновений.
const noDrag = this.matter.world.nextGroup();
this.matter.add.image(200, 100, 'mushroom', null, { chamfer: 16 }).setBounce(0.9).setCollisionGroup(noDrag);
Настройка мышиной пружины с фильтром
Секрет избирательного перетаскивания кроется в создании мышиной пружины — инструмента, который связывает указатель мыши с физическим телом в мире Matter.js.
this.matter.add.mouseSpring({ length: 1, stiffness: 0.6, collisionFilter: { group: canDrag } });
При создании пружины через this.matter.add.mouseSpring() в конфигурации указывается объект collisionFilter. В его свойстве group передается значение canDrag — та самая группа, которую мы создали для блоков. Это означает, что пружина будет "цепляться" и создавать соединение только с телами, чья группа столкновений (collisionFilter.group) совпадает с указанной. Тела из группы noDrag (грибы) или любые другие тела с иным значением группы игнорируются. Параметры length (длина) и stiffness (жесткость) влияют на поведение самой пружины при перетаскивании.
Сборка сцены и конфигурация игры
Важно правильно инициализировать физический движок Matter для всей игры. Это делается в конфигурационном объекте.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#1b1464',
parent: 'phaser-example',
physics: {
default: 'matter', // Активация Matter.js
matter: {
// Здесь можно добавить дополнительные настройки Matter
}
},
scene: Example
};
Вызов this.matter.world.setBounds() в начале create() автоматически создает статические границы (стены) вокруг мира, соответствующие размерам игрового поля, чтобы тела не улетали за его пределы.
Что попробовать дальше
Использование групп столкновений Matter.js для фильтрации взаимодействия — мощный и чистый метод управления игровой механикой. Он позволяет гибко разделять объекты по их поведению. Вы можете экспериментировать: создать несколько разных мышиных пружин с разными фильтрами для различных инструментов (например, магнит только для металлических предметов), или динамически менять группу у тела в runtime, чтобы включать и выключать возможность перетаскивания в зависимости от игровых событий.
