Matter.js selecting multiple Bodies with mouse drag

Viewed 88

I'm attempting to create an HTML version of the Disney Tsum Tsum game with Matter.js

Game reference - https://www.youtube.com/watch?v=iqQrQtorM9c

But currently, I can't figure out how to identify the Bodies that have been selected when the player drags across a couple of them. It seems that the mouse constraints only identify the first Body it is clicked on only.

1 Answers

One approach is to detect when the mouse is down by flipping a flag, then use Matter.Query.point to query the current mouse location whenever a drag occurs, pushing any bodies onto an array. Upon mouse release, the array will contain the selected bodies for that drag and you can empty it out after taking an action on its contents.

Here's a proof of concept:

const engine = Matter.Engine.create();
const render = Matter.Render.create({
  element: document.body,
  engine: engine,
  options: {wireframes: false},
});
const bodies = [
  Matter.Bodies.rectangle(
    400, 200, 810, 60, {isStatic: true, angle: 0.0}
  ),
  ...[...Array(100)].map(() =>
    Matter.Bodies.circle(
      Math.random() * 400,     // x
      Math.random() * 100,     // y
      Math.random() * 10 + 10, // r
      {
        angle: Math.random() * (Math.PI * 2),
        render: {fillStyle: "white"}
      },
    )
  ),
];
const mouseConstraint = Matter.MouseConstraint.create(
  engine, {
    element: document.body,
    collisionFilter: {mask: 0} // disable collision
  }
);
const runner = Matter.Runner.create();
let mouseDown = false;
const selected = [];
Matter.Events.on(mouseConstraint, "mousedown", () => {
  mouseDown = true;
  addSelection();
});
Matter.Events.on(mouseConstraint, "mouseup", () => {
  mouseDown = false;
  selected.forEach(e => e.render.fillStyle = "red");
  selected.length = 0;
});
Matter.Events.on(mouseConstraint, "mousemove", () => {
  if (mouseDown) {
    addSelection();
  }
});
const addSelection = () => {
  const p = mouseConstraint.mouse.position;
  const justSelected = Matter.Query.point(bodies, p);
  selected.push(...justSelected);
  justSelected.forEach(e => e.render.fillStyle = "yellow");
};
Matter.Composite.add(engine.world, [...bodies, mouseConstraint]);
Matter.Runner.start(runner, engine);
Matter.Render.run(render);
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.js"></script>

If you're not using the internal renderer, the concept is the same.

Related