Reputation: 1
I want a way to see if my player (which is a box) is colliding with another body from the world, any other body than my player.
I didn't find a way to do it in the documentation, only in pair or in a list. But if I put the entire world in the list, it's going to count other collisions without the player.
Upvotes: 0
Views: 158
Reputation: 57289
You can use Matter.Query.collides(boxBody, arrayOfAllOtherEntitiesExceptBoxBody)
. By excluding the player box from the second parameter collision array, there won't be a false positive self-collision on the player box.
Query.collides()
returns an array of collisions, so it may be a bit expensive if you just need a boolean (I'm not sure how it's actually implemented, just a guess. It's good not to prematurely optimize). Matter.Events.on(engine, "collisionStart", cb)
can be used as an alternative. In the callback, iterate the colliding body pairs to see if any are the player.
If use Query.collides()
, you'll likely run it on collisionStart
and collisionEnd
rather than beforeUpdate
to avoid running it more than necessary.
Here's a minimal example illustrating both techniques. Use the mouse to move the player.
const engine = Matter.Engine.create();
engine.gravity.y = 0; // enable top-down
const map = {width: 300, height: 300};
const render = Matter.Render.create({
element: document.body,
engine,
options: {...map, wireframes: false},
});
const player = Matter.Bodies.rectangle(
map.width / 2, map.height / 2, 25, 25, {
render: {fillStyle: "green"},
},
);
const rnd = (lo, hi) => Math.random() * (hi - lo) + lo;
const enemies = [...Array(25)].map(() =>
Matter.Bodies.circle(
rnd(0, map.width),
rnd(0, map.height),
rnd(5, 15),
{
isSensor: true,
render: {fillStyle: "yellow"}
}
)
);
Matter.Composite.add(engine.world, [...enemies, player]);
Matter.Events.on(engine, "collisionEnd", event => {
const collisions = Matter.Query.collides(player, enemies);
if (collisions.length === 0) {
player.render.fillStyle = "green";
}
});
Matter.Events.on(engine, "collisionStart", event => {
for (const {bodyA, bodyB} of event.pairs) {
if (bodyA === player || bodyB === player) {
player.render.fillStyle = "red";
}
}
});
document
.querySelector("canvas")
.addEventListener("mousemove", ({offsetX: x, offsetY: y}) => {
Matter.Body.setPosition(player, {x, y});
});
Matter.Render.run(render);
Matter.Runner.run(Matter.Runner.create(), engine);
body {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.min.js"></script>
Yet another option is to tap into the collisionActive
event. Here's an example that would work in the above code:
Matter.Events.on(engine, "collisionActive", event => {
const collisionWithPlayer = event.pairs.find(e =>
e.bodyA === player || e.bodyB === player
);
console.log(!!collisionWithPlayer);
});
.some()
can be used instead of .find()
if you only care about getting the boolean. Or if you want an array of all collisions between the player and other bodies:
Matter.Events.on(engine, "collisionActive", event => {
const collisionsWithPlayer = event.pairs.filter(e =>
e.bodyA === player || e.bodyB === player
);
console.log(collisionsWithPlayer.length);
});
Upvotes: 1