stackers
stackers

Reputation: 3279

How can I change the collisionFilter of an object so it can no longer interact with MouseConstraint (MatterJS)

I am working off the Slingshot demo. The problem is that after the rock is fired, it can still be clicked on and dragged around, which I want to disable.

I've added a filter to the rock:

var rockOptions = {
  density: 0.004,
  restitution: 0.75,
  collisionFilter: { mask: SOLID, category: NEXTBALL }
};

And to the mouseconstraint:

var mouse = Mouse.create(render.canvas),
  mouseConstraint = MouseConstraint.create(engine, {
    mouse: mouse,
    collisionFilter: { category: NEXTBALL },
    constraint: {
      stiffness: 0.2,
      render: {
        visible: true
      }
    }
  });

And then in the click event I attempt to change that filter, so it should no longer match the mouses category:

Events.on(engine, "afterUpdate", function () {
  if (
    mouseConstraint.mouse.button === -1 &&
    (rock.position.x > shootPosition.x + 20 ||
      rock.position.y < shootPosition.y - 20)
  ) {
    Composite.remove(engine.world, elastic);

    rock.collisionFilter = {category: SOLID, mask: SOLID};
  }
});

But it still is draggable. I'm guessing the problem is how I'm changing the filter on the rock, but I don't see anything in the docs to suggest a way to change it.

I don't think it's because of the categories I've set up, but here they are just in case (the solid and image ones do work, the ball doesn't collide with image ones:

const SOLID = 0x0001;
const IMAGE = 0x0002;
const NEXTBALL = 0x0003;

Help me make the rock stop being clickable

Upvotes: 0

Views: 841

Answers (2)

ggorlen
ggorlen

Reputation: 57195

Preventing the mouse from manipulating already-shot rocks can be achieved with two simple changes denoted by // <--- in the code snippet below.

  • rock.collisionFilter.category = 0b10; sets the category to 2 for any rock that was just launched, before creating a new one and overwriting the rock variable with the next rock to be launched.
  • collisionFilter: {mask: 0b1}, sets the mask on the mouse constraint to only interact with bodies in category 1. Since disabled rocks are in category 2 (0b10), the mouse will no longer interact with them.

Here's the code in context. I used this commit in case anything changes.

Events.on(engine, 'afterUpdate', function() {
  if (mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) {
    rock.collisionFilter.category = 0b10; // <---
    rock = Bodies.polygon(170, 450, 7, 20, rockOptions);
    Composite.add(engine.world, rock);
    elastic.bodyB = rock;
  }
});

// add mouse control
var mouse = Mouse.create(render.canvas),
  mouseConstraint = MouseConstraint.create(engine, {
    mouse: mouse,
    collisionFilter: {mask: 0b1}, // <---
    constraint: {
      stiffness: 0.2,
      render: {
        visible: false
      }
    }
  });

The default mask value is 32 bits all set, or 4294967295/0xffffffff. You may wish to be more precise and disable only the second bit on the mouse constraint: 0xfffffffd. This lets the mouse interact with anything but category 2 rather than just category 1.

The existing answer is correct that categories should be powers of two so that only one bit is set per category.

The reason rock.collisionFilter = {category: SOLID, mask: SOLID}; doesn't work is because it destroys the group property.

See also:

Upvotes: 2

stackers
stackers

Reputation: 3279

Finally figured it out after much starting over, tweaking and examining different demos.

First the bitmasks for the categories have to be powers of two, so NEXTBALL has to be 0x0004 rather than 0x0003.

Next, you can't set the whole collisionFilter object on an established body, or it breaks the collisions. Instead, you have to use rock.collisionFilter.category = NEXTBALL;

Upvotes: 1

Related Questions