О чем этот пример
Встроенная гравитация в физических движках действует глобально и однонаправленно. Но что, если вам нужно создать локальный источник притяжения, вроде чёрной дыры или магнита? В этом поможет система аттракторов 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('sun', 'assets/tests/space/sun.png');
this.load.image('alien', 'assets/sprites/space-baddie.png');
}
create ()
{
this.matter.world.setBounds();
this.matter.add.imageStack('alien', null, 0, 500, 50, 2, 0, 0, {
mass: 1,
ignorePointer: true
});
const sun = this.matter.add.image(400, 200, 'sun', null, {
shape: {
type: 'circle',
radius: 64
},
attractors: [
(bodyA, bodyB) => ({
x: (bodyA.position.x - bodyB.position.x) * 0.000001,
y: (bodyA.position.y - bodyB.position.y) * 0.000001
})
]
});
this.matter.add.mouseSpring();
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
physics: {
default: 'matter',
matter: {
gravity: {
scale: 0
},
}
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка сцены и загрузка ассетов
В методе preload мы загружаем два изображения: 'sun' (солнце) в качестве аттрактора и 'alien' (пришелец) в качестве притягиваемых тел. Базовый URL задаётся для удобства.
В методе create первым делом устанавливаем границы мира Matter.js, чтобы тела не улетали за пределы экрана. Глобальная гравитация отключена в конфигурации игры (gravity: { scale: 0 }), так как всю работу будет выполнять наш аттрактор.
// В config.physics.matter
gravity: {
scale: 0
}
// В методе create
this.matter.world.setBounds();
Создание толпы притягиваемых тел
Вместо создания множества спрайтов по одному, используем удобный метод this.matter.add.imageStack. Он создаёт сетку или столбец из одинаковых физических тел.
В нашем примере мы создаём 50 тел в 2 ряда, начиная с координаты (0, 500). Ключевой параметр ignorePointer: true отключает взаимодействие этих тел с курсором мыши, чтобы они не мешали работе mouseSpring.
this.matter.add.imageStack('alien', null, 0, 500, 50, 2, 0, 0, {
mass: 1,
ignorePointer: true
});
Сердце системы: создание тела-аттрактора
Самое важное — создание тела sun, которое будет притягивать остальные. Обратите внимание на два ключевых свойства, передаваемых в конфигурационном объекте.
Свойство shape определяет физическую коллизию тела. Мы задаём её как круг радиусом 64 пикселя, совпадающий с размером изображения.
Свойство attractors — это массив функций. Каждая функция будет вызываться движком Matter.js для расчёта силы притяжения между данным телом (bodyA) и любым другим телом в мире (bodyB).
const sun = this.matter.add.image(400, 200, 'sun', null, {
shape: {
type: 'circle',
radius: 64
},
attractors: [
(bodyA, bodyB) => ({
x: (bodyA.position.x - bodyB.position.x) * 0.000001,
y: (bodyA.position.y - bodyB.position.y) * 0.000001
})
]
});
Как работает функция-аттрактор
Функция-аттрактор обязана возвращать объект с вектором силы { x, y }, которая будет применена к bodyB (притягиваемому телу).
В нашем примере логика проста:
1. Вычисляется разница между координатами аттрактора (bodyA.position) и притягиваемого тела (bodyB.position). Это вектор, указывающий *от* bodyB *к* bodyA.
2. Этот вектор умножается на очень маленький коэффициент (0.000001), определяющий силу притяжения. Результат — слабая сила, толкающая bodyB в сторону bodyA.
Таким образом, для каждого кадра и для каждой пары тел (sun и каждый alien) вычисляется и применяется эта сила, создавая эффект гравитационного притяжения.
(bodyA, bodyB) => ({
x: (bodyA.position.x - bodyB.position.x) * 0.000001,
y: (bodyA.position.y - bodyB.position.y) * 0.000001
})
Интерактивность: добавляем пружину к курсору
Чтобы можно было взаимодействовать с системой, добавляем this.matter.add.mouseSpring(). Этот хелмер создаёт невидимую пружину, которая связывает курсор мыши с ближайшим физическим телом, у которого не установлен флаг ignorePointer. В нашем случае это будет только тело sun.
Это позволяет "таскать" за собой солнце и наблюдать, как вся масса пришельцев начинает двигаться вслед за ним, продолжая подчиняться законам аттрактора.
this.matter.add.mouseSpring();
Что попробовать дальше
Аттракторы Matter.js открывают путь к созданию сложных силовых полей прямо внутри вашей игры. Вы можете модифицировать функцию-аттрактор для создания отталкивания, зависящей от расстояния силы (закон обратных квадратов) или даже условий (например, притягивать только тела определённой категории). Попробуйте экспериментировать: создайте несколько тел с разными аттракторами, измените коэффициент силы на динамический (в зависимости от расстояния между телами) или добавьте условие, чтобы сила действовала только в определённом радиусе.
