Search code examples
javascriptmatter.js

How to allow only a single body to move using Matter-js Mouse


So I am making an Angry-Birds game and I am using p5.js and matter.js.

I created a mouseConstraint in the game to move the bird attached to a slingshot, but I am also able to move all the bodies in the output.

How can I attach the mouseConstraint only to a single body, i.e., the bird in this case, so that I can move only that particular object and nothing else?

If this is not possible, is there an alternative which I can use for using the slingshot?


Solution

  • You can use collision filters:

    const makeBox = (x, y, w, h, props, elem) => ({
      w, h, body: Matter.Bodies.rectangle(
        x, y, w, h, props
      ),
      elem,
      render() {
        const {x, y} = this.body.position;
        this.elem.style.top = `${y - this.h / 2}px`;
        this.elem.style.left = `${x - this.w / 2}px`;
        this.elem.style.transform = `rotate(${this.body.angle}rad)`;
      },
    });
    const boxes = [...document.querySelectorAll(".box")]
      .map((el, i) =>
        makeBox(
        // x             y  w   h
          100 * i + 100, 0, 40, 30,
          {collisionFilter: i === 0 ? {category: 0b10} : {}},
          el,
        )
      );
    const ground = Matter.Bodies.rectangle(
      // x    y    w    h
         200, 200, 400, 120, {
        isStatic: true,
      }
    );
    const engine = Matter.Engine.create();
    const mouseConstraint = Matter.MouseConstraint.create(
      engine, {
        collisionFilter: {mask: 0b10},
        element: document.body
      }
    );
    Matter.Composite.add(
      engine.world, [
        ...boxes.map(e => e.body), ground, mouseConstraint
      ]
    );
    (function rerender() {
      boxes.forEach(e => e.render());
      Matter.Engine.update(engine);
      requestAnimationFrame(rerender);
    })();
    .box {
      position: absolute;
      background: #d00;
      transition: background 0.2s;
      width: 40px;
      height: 30px;
      cursor: move;
    }
    .box:not(:first-child) {
      background: #111;
      cursor: not-allowed;
    }
    .box:first-child:hover {
      background: #f00;
    }
    
    #ground {
      position: absolute;
      background: #666;
      top: 140px;
      height: 120px;
      width: 400px;
    }
    
    html, body {
      position: relative;
      height: 100%;
      margin: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.min.js"></script>
    <div>
      <div class="box"></div>
      <div class="box"></div>
      <div class="box"></div>
    </div>
    <div id="ground"></div>

    Here, the mouse constraint is given a mask of 0b10 and the red box which is the only body that is allowed to interact with the mouse is set to a category of 0b10.

    The default mask value is 32 bits all set, or 4294967295/0xffffffff. You may wish to be more precise and disable only the first bit: 0xfffffffe. This lets the mouse constraint interact with other categories in addition to 2, disabling only interactions with category 1.

    To create the opposite situation, where the mouse interacts with any bodies except for the red box, you can set the mouse constraint's mask to something that has the secondmost least significant bit off, like 0b1 or 0xfffffffd.

    See also: