О чем этот пример
Физика — ключевой элемент многих игр. Библиотека Matter.js, интегрированная в Phaser 3, позволяет моделировать сложные физические взаимодействия, такие как мягкие тела, составные конструкции и соединительные элементы. В этой статье мы разберем пример создания динамического моста из прямоугольников, соединенных цепями, на который можно сбрасывать объекты. Вы узнаете, как использовать возможности `matter.add.stack`, `matter.add.chain` и `worldConstraint` для построения интерактивных конструкций, реагирующих на физику мира.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create ()
{
this.matter.world.setBounds();
this.matter.add.mouseSpring();
const group = this.matter.world.nextGroup(true);
const bridge = this.matter.add.stack(160, 290, 15, 1, 0, 0, (x, y) => Phaser.Physics.Matter.Matter.Bodies.rectangle(x - 20, y, 53, 20, {
collisionFilter: { group: group },
chamfer: 5,
density: 0.005,
frictionAir: 0.05
}));
this.matter.add.chain(bridge, 0.3, 0, -0.3, 0, {
stiffness: 1,
length: 0,
render: {
visible: false
}
});
const stack = this.matter.add.stack(250, 50, 6, 3, 0, 0, (x, y) => Phaser.Physics.Matter.Matter.Bodies.rectangle(x, y, 50, 50, Phaser.Math.Between(20, 40)));
this.matter.add.rectangle(30, 490, 220, 380, {
isStatic: true,
chamfer: { radius: 20 }
}),
this.matter.add.rectangle(770, 490, 220, 380, {
isStatic: true,
chamfer: { radius: 20 }
}),
this.matter.add.worldConstraint(bridge.bodies[0], 2, 0.9, {
pointA: { x: 140, y: 300 },
pointB: { x: -25, y: 0 }
});
this.matter.add.worldConstraint(bridge.bodies[bridge.bodies.length - 1], 2, 0.9, {
pointA: { x: 660, y: 300 },
pointB: { x: 25, y: 0 }
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
physics: {
default: 'matter',
matter: {
gravity: {
y: 0.8
},
debug: true,
debugBodyColor: 0xffffff
}
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка физического мира
Перед созданием объектов необходимо настроить физический мир. В методе create() сцены мы задаем границы мира и добавляем интерактивность в виде пружины, привязанной к курсору мыши.
this.matter.world.setBounds();
this.matter.add.mouseSpring();
Вызов setBounds() создает статические границы по краям мира, чтобы объекты не вылетали за пределы экрана. mouseSpring() добавляет пружину, соединенную с курсором мыши, позволяя перетаскивать и бросать физические тела — это полезно для тестирования и интерактивности. Настройки физики, включая гравитацию и отладочную визуализацию, задаются в конфигурации игры.
Создание группы тел и моста
Мост будет состоять из серии прямоугольных тел, соединенных в цепочку. Для этого сначала создается группа столкновений, чтобы тела моста взаимодействовали друг с другом.
const group = this.matter.world.nextGroup(true);
Метод nextGroup(true) генерирует уникальную группу для фильтра столкновений, гарантируя, что тела внутри этой группы будут сталкиваться друг с другом. Далее создается стек из 15 прямоугольных тел, которые станут сегментами моста.
const bridge = this.matter.add.stack(160, 290, 15, 1, 0, 0, (x, y) => Phaser.Physics.Matter.Matter.Bodies.rectangle(x - 20, y, 53, 20, {
collisionFilter: { group: group },
chamfer: 5,
density: 0.005,
frictionAir: 0.05
}));
Функция stack создает сетку из тел. Параметры: начальные координаты (160, 290), количество тел по горизонтали (15) и вертикали (1), шаг по X и Y (0, 0). Последний аргумент — функция, создающая каждое тело. Здесь используется Matter.Bodies.rectangle с настройками: смещение по X (-20), размеры (53x20), скругление углов (chamfer: 5), низкая плотность (density: 0.005) и небольшое сопротивление воздуха (frictionAir: 0.05). Это делает мост легким и гибким.
Соединение тел в цепь
Чтобы сегменты моста образовывали единую гибкую конструкцию, их нужно соединить. Для этого используется метод matter.add.chain.
this.matter.add.chain(bridge, 0.3, 0, -0.3, 0, {
stiffness: 1,
length: 0,
render: {
visible: false
}
});
Метод chain соединяет все тела в массиве bridge. Параметры 0.3 и -0.3 задают смещение точек соединения на каждом теле по оси X, что создает перекрытие сегментов. Объект конфигурации определяет свойства соединений: stiffness: 1 (максимальная жесткость, чтобы мост не растягивался), length: 0 (нулевая длина соединения) и render: { visible: false } (скрывает визуальное отображение соединений).
Добавление статических опор и динамических объектов
Мосту нужны опоры, к которым он будет крепиться, и объекты для взаимодействия. Сначала создаются две статические прямоугольные опоры по краям экрана.
this.matter.add.rectangle(30, 490, 220, 380, {
isStatic: true,
chamfer: { radius: 20 }
});
this.matter.add.rectangle(770, 490, 220, 380, {
isStatic: true,
chamfer: { radius: 20 }
});
Параметр isStatic: true делает тело неподвижным, а chamfer скругляет углы. Затем создается стек из 18 динамических прямоугольников, которые будут падать на мост.
const stack = this.matter.add.stack(250, 50, 6, 3, 0, 0, (x, y) => Phaser.Physics.Matter.Matter.Bodies.rectangle(x, y, 50, 50, Phaser.Math.Between(20, 40)));
Здесь функция создания тела использует Phaser.Math.Between(20, 40) для случайного скругления углов каждого прямоугольника, добавляя визуальное разнообразие.
Крепление моста к миру с помощью ограничений
Чтобы мост был закреплен на опорах, используются мировые ограничения (worldConstraint). Они привязывают первый и последний сегменты моста к фиксированным точкам в мире.
this.matter.add.worldConstraint(bridge.bodies[0], 2, 0.9, {
pointA: { x: 140, y: 300 },
pointB: { x: -25, y: 0 }
});
this.matter.add.worldConstraint(bridge.bodies[bridge.bodies.length - 1], 2, 0.9, {
pointA: { x: 660, y: 300 },
pointB: { x: 25, y: 0 }
});
Метод worldConstraint принимает тело (первый или последний элемент массива bridge.bodies), длину ограничения (2), жесткость (0.9) и объект с точками привязки. pointA — это точка в мировых координатах, где находится ограничение (возле опор), а pointB — точка на самом теле (смещение от его центра). Это создает эффект 'подвешивания' концов моста.
Что попробовать дальше
Вы создали динамический мост на Matter.js, используя комбинацию стеков тел, цепей и мировых ограничений. Такой подход позволяет моделировать гибкие конструкции, качели, цепи или тросы. Для экспериментов попробуйте изменить параметры: увеличьте frictionAir для большего сопротивления, настройте stiffness цепи для создания эластичного моста или замените прямоугольники на другие формы. Добавьте события столкновений, чтобы объекты, падающие на мост, издавали звуки или меняли цвет.
