О чем этот пример
Создание реалистичных цепочек, хлыстов или змеевидных существ — классическая задача игровой физики. В этом примере используется движок Matter.js для Phaser 3, чтобы быстро построить цепочку связанных тел, ведущую себя как единая гибкая структура. Вы узнаете, как создавать динамические соединения, настраивать отладочную визуализацию и отключать гравитацию для полного контроля над симуляцией.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
create ()
{
// this.matter.world.setBounds().disableGravity();
this.matter.world.disableGravity();
this.matter.add.mouseSpring();
let x = 100;
let y = 300;
let prevBody;
for (let i = 0; i < 8; i++)
{
const body = this.matter.add.circle(x, y, 24);
if (i > 0)
{
const s = this.matter.add.spring(body, prevBody, 48, 0.9, { damping: 0.9, angularStiffness: 0.9 });
}
prevBody = body;
x += 64;
}
}
update ()
{
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#1b1464',
parent: 'phaser-example',
physics: {
default: 'matter',
matter: {
enableSleeping: true,
debug: {
showAxes: false,
showAngleIndicator: true,
angleColor: 0xe81153,
showBroadphase: false,
broadphaseColor: 0xffb400,
showBounds: false,
boundsColor: 0xffffff,
showVelocity: true,
velocityColor: 0x00aeef,
showCollisions: true,
collisionColor: 0xf5950c,
showSeparations: false,
separationColor: 0xffa500,
showBody: true,
showStaticBody: true,
showInternalEdges: true,
renderFill: false,
renderLine: true,
fillColor: 0x106909,
fillOpacity: 1,
lineColor: 0x28de19,
lineOpacity: 1,
lineThickness: 1,
staticFillColor: 0x0d177b,
staticLineColor: 0x1327e4,
showSleeping: true,
staticBodySleepOpacity: 1,
sleepFillColor: 0x464646,
sleepLineColor: 0x999a99,
showSensors: true,
sensorFillColor: 0x0d177b,
sensorLineColor: 0x1327e4,
showPositions: true,
positionSize: 4,
positionColor: 0xe042da,
showJoint: true,
jointColor: 0xe0e042,
jointLineOpacity: 1,
jointLineThickness: 2,
pinSize: 4,
pinColor: 0x42e0e0,
springColor: 0xe042e0,
anchorColor: 0xefefef,
anchorSize: 4,
showConvexHulls: true,
hullColor: 0xd703d0
}
}
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка: настройка физики
Вся магия начинается с конфигурации игры. Мы используем движок Matter.js (default: 'matter'), который позволяет работать с продвинутой физикой твердых тел. Ключевая особенность — включение детального отладочного режима. В примере он настроен так, чтобы подсвечивать скорости, столкновения и сами тела, что невероятно полезно при отладке физических взаимодействий.
const config = {
type: Phaser.AUTO,
physics: {
default: 'matter',
matter: {
enableSleeping: true,
debug: {
showVelocity: true,
showCollisions: true,
showBody: true,
// ... остальные настройки визуализации
}
}
},
scene: Example
};
Сцена: отключаем гравитацию и добавляем управление
В методе create() сцены мы подготавливаем мир. Строка this.matter.world.disableGravity() отключает глобальную гравитацию. Это нужно, чтобы наша цепочка не падала вниз и мы могли наблюдать за ее поведением в изоляции.
Следующая важная строка — this.matter.add.mouseSpring(). Она создает пружинное соединение между курсором мыши и любым телом, за которое вы перетащите. Это позволяет интерактивно дергать и растягивать нашу змейку.
create ()
{
this.matter.world.disableGravity();
this.matter.add.mouseSpring();
// ... создание цепочки
}
Создание цепочки тел
Сердце примера — цикл, создающий восемь круглых тел. Мы используем this.matter.add.circle(x, y, 24) для добавления каждого нового тела (шара) с радиусом 24 пикселя. Координата X увеличивается на 64 пикселя с каждой итерацией, выстраивая тела в линию.
let x = 100;
let y = 300;
let prevBody;
for (let i = 0; i < 8; i++)
{
const body = this.matter.add.circle(x, y, 24);
// ... создание пружины
prevBody = body;
x += 64;
}
Связывание тел пружинными соединениями
Чтобы тела не разлетелись, их нужно соединить. Начиная со второго тела (i > 0), мы связываем текущее тело с предыдущим с помощью this.matter.add.spring(). Эта функция создает пружинное соединение (джоинт) между двумя физическими телами.
Параметры соединения:
- body, prevBody: Соединяемые тела.
- 48: Длина покоя пружины. Если тела находятся ближе или дальше этого расстояния, пружина будет стремиться вернуть их на место.
- 0.9: Жесткость пружины (stiffness). Определяет, насколько сильно пружина сопротивляется сжатию или растяжению.
- { damping: 0.9, angularStiffness: 0.9 }: Демпфирование гасит колебания, делая движение более плавным и предотвращая "дребезг". angularStiffness влияет на жесткость при угловом (вращательном) смещении.
if (i > 0)
{
const s = this.matter.add.spring(body, prevBody, 48, 0.9, { damping: 0.9, angularStiffness: 0.9 });
}
Что попробовать дальше
Вы создали базовую модель мягкого, сегментированного тела, реагирующего на физику и взаимодействие с мышью. Это основа для множества игровых объектов: цепей, щупалец, хвостов драконов или даже реалистичных волос. Для экспериментов попробуйте изменить параметры пружин (жесткость, демпфирование), добавить больше сегментов, включить гравитацию (this.matter.world.setGravity(0, 1)) или прикрепить первый сегмент к статичной точке опоры с помощью this.matter.add.constraint().
