О чем этот пример
При разработке игр с физикой часто возникает необходимость определить, с каким именно объектом взаимодействует игрок. Например, для нанесения урона по врагу или активации механизма. Встроенный метод `intersectPoint` в Phaser с плагином Matter.js позволяет легко и эффективно решить эту задачу. В этой статье мы разберем пример сцены, которая создает комплексный физический мир со статическими и динамическими телами, соединениями разного типа, и покажем, как по координатам клика мыши получить массив всех физических тел в этой точке.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor()
{
super();
}
create()
{
this.matter.world.setBounds();
// First, we'll create a few static bodies
const body1 = this.matter.add.rectangle(250, 50, 200, 32, { isStatic: true });
this.matter.add.polygon(600, 100, 3, 40, { isStatic: true });
this.matter.add.polygon(100, 500, 8, 50, { isStatic: true });
this.matter.add.rectangle(750, 200, 16, 180, { isStatic: true });
// Now a body that shows off internal edges + convex hulls
const star = '50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38';
this.matter.add.fromVertices(700, 500, star, { restitution: 0.9 }, true);
// Some different joint types
const body2 = this.matter.add.circle(150, 250, 16);
const body3 = this.matter.add.circle(400, 450, 16);
const body4 = this.matter.add.circle(500, 50, 16);
// A spring, because length > 0 and stiffness < 0.9
this.matter.add.spring(body1, body2, 140, 0.001);
// A joint, because length > 0 and stiffness > 0.1
this.matter.add.worldConstraint(body3, 140, 1, {
pointA: {
x: 400, y: 250
}
});
// A pin, because length = 0 and stiffness > 0.1
this.matter.add.worldConstraint(body4, 0, 1, {
pointA: {
x: 500, y: 50
}
});
// Finally some random dynamic bodies
for (let i = 0; i < 12; i++)
{
const x = Phaser.Math.Between(100, 700);
const y = Phaser.Math.Between(100, 500);
if (Math.random() < 0.5)
{
const sides = Phaser.Math.Between(3, 14);
const radius = Phaser.Math.Between(8, 50);
this.matter.add.polygon(x, y, sides, radius, { restitution: 0.9 });
}
else
{
const width = Phaser.Math.Between(16, 128);
const height = Phaser.Math.Between(8, 64);
this.matter.add.rectangle(x, y, width, height, { restitution: 0.9 });
}
}
this.input.on('pointerdown', function (pointer, gameObject) {
const bodies = this.matter.intersectPoint(pointer.worldX, pointer.worldY);
if (bodies.length)
{
console.log(bodies);
}
}, this);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#4d4d4d',
parent: 'phaser-example',
physics: {
default: 'matter',
matter: {
enableSleeping: false,
gravity: {
y: 0
},
debug: {}
}
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка физического мира Matter.js
Перед созданием тел необходимо настроить физический мир. В конфигурации игры (config) указывается, что используется физический движок matter. Важные настройки включают отключение силы тяжести (gravity.y: 0) для статичной демонстрации и включение отладки (debug: {}), что поможет визуализировать тела при разработке.
В методе create сцены первым делом устанавливаются границы мира с помощью this.matter.world.setBounds().
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#4d4d4d',
parent: 'phaser-example',
physics: {
default: 'matter',
matter: {
enableSleeping: false,
gravity: { y: 0 },
debug: {}
}
},
scene: Example
};
create() {
this.matter.world.setBounds();
// ... создание тел
}
Создание разнообразных физических тел
Matter.js в Phaser предоставляет фабричные методы для создания тел разных форм. Тела могут быть статическими (isStatic: true) — они не двигаются под воздействием сил, и динамическими.
В примере создаются:
* Статические тела: прямоугольник (rectangle), треугольник (polygon с 3 сторонами), восьмиугольник и тонкий прямоугольник-столб.
* Динамическое тело в форме звезды из вершин (fromVertices). Параметр true указывает на необходимость построения выпуклой оболочки.
* Динамические тела-шары (circle), которые будут соединены с миром.
* Случайные динамические тела (многоугольники и прямоугольники), разбросанные по сцене.
// Статический прямоугольник
const body1 = this.matter.add.rectangle(250, 50, 200, 32, { isStatic: true });
// Динамическая звезда из вершин
this.matter.add.fromVertices(700, 500, star, { restitution: 0.9 }, true);
// Динамический круг
const body2 = this.matter.add.circle(150, 250, 16);
// Случайный многоугольник
this.matter.add.polygon(x, y, sides, radius, { restitution: 0.9 });
Соединения: Пружины, joints и пины
Matter.js позволяет связывать тела между собой или тело с точкой в мире. В примере показаны три типа соединений, создаваемых через this.matter.add.spring и this.matter.add.worldConstraint.
1. **Пружина (Spring)**: Соединяет body1 и body2. Задается длиной (140) и низкой жесткостью (0.001), что позволяет телу колебаться.
2. **Жесткое соединение (Joint)**: Соединяет body3 с фиксированной точкой в мире (pointA). Имеет длину (140) и высокую жесткость (`1), поэтому тело будет вращаться вокруг точкиpointA` как на жестком стержне.
3. **Шарнир (Pin)**: Соединяет body4 с фиксированной точкой в мире. Нулевая длина (`0) и высокая жесткость (1) означают, что тело закреплено в точкеpointA`, но может свободно вращаться вокруг нее.
// Пружина
this.matter.add.spring(body1, body2, 140, 0.001);
// Жесткое соединение (Joint)
this.matter.add.worldConstraint(body3, 140, 1, {
pointA: { x: 400, y: 250 }
});
// Шарнир (Pin)
this.matter.add.worldConstraint(body4, 0, 1, {
pointA: { x: 500, y: 50 }
});
Определение тел под точкой с помощью `intersectPoint`
Ключевая функциональность примера — обработка клика мыши. При нажатии (pointerdown) вычисляются мировые координаты курсора (pointer.worldX, pointer.worldY).
Метод this.matter.intersectPoint() принимает эти координаты и возвращает **массив всех физических тел движка Matter.js**, которые в данный момент пересекаются с данной точкой. Это мощный инструмент для обработки кликов в сложных физических мирах, где тела могут перекрывать друг друга.
Если массив не пуст, его содержимое (объекты тел) выводится в консоль для отладки.
this.input.on('pointerdown', function (pointer, gameObject) {
const bodies = this.matter.intersectPoint(pointer.worldX, pointer.worldY);
if (bodies.length) {
console.log(bodies);
}
}, this);
Что попробовать дальше
Метод intersectPoint — это простой и прямой способ реализовать точное взаимодействие с физическими объектами в Phaser при использовании Matter.js. Он идеально подходит для игр, где важна точность: стратегиях, головоломках или симуляторах.
Для экспериментов попробуйте:
1. В обработчике клика не просто выводить тела в консоль, а применять к ним силу (body.applyForce) или менять их свойства (например, делать статическими).
2. Реализовать систему выделения объекта под курсор с визуальной подсветкой найденного тела.
3. Комбинировать intersectPoint с рейкастингом (raycast) для более сложных механик, например, определения первого тела на пути снаряда.
