First of all, World is deprecated so you should be using Composite.remove instead of World.remove.
As for the error, setStatic is a Body method, not a Bodies method. You could loop over each body and call setStatic on it: boats.forEach(e => Matter.Body.setStatic(e, true));, but according to this issue, you won't be able to toggle staticness safely without implementing caching for the bodies or other workaround. See How do I make a matter body isStatic false after pressing a key in matter.js? for more potential options.
Are you sure you want to implement pausing by setting bodies to static? This doesn't really pause the simulation--time keeps ticking and various properties might continue to change in unexpected ways.
Since you're using your own renderer (p5.js), pausing the simulation as a whole is straightforward. You can stop calling your MJS code in the rendering loop and the simulation will pause. In p5.js, this can be done with a boolean check at the beginning of draw, or by setting draw temporarily to a function that doesn't run MJS updates (and probably instead shows a menu or "paused" screen). See Transitioning from one scene to the next with p5.js for details on that approach.
Here's a basic example of toggling pause via an early return in the draw() loop based on the value of a checkbox:
class Ball {
constructor(x, y, r) {
const options = {
restitution: 0.1,
density: 1.5,
friction: 1,
};
this.body = Matter.Bodies.circle(x, y, r, options);
}
draw() {
const {position: {x, y}, circleRadius: r} = this.body;
fill("white");
ellipse(x, y, r * 2);
}
}
const engine = Matter.Engine.create();
const balls = [...Array(20)].map((_, i) =>
new Ball(
50 + ~~(Math.random() * 450),
50 + ~~(Math.random() * 150),
~~(Math.random() * 5) + 10,
)
);
const walls = [
Matter.Bodies.rectangle(
250, 200, 500, 50, {isStatic: true}
),
Matter.Bodies.rectangle(
250, 0, 500, 50, {isStatic: true}
),
Matter.Bodies.rectangle(
0, 100, 50, 200, {isStatic: true}
),
Matter.Bodies.rectangle(
500, 100, 50, 200, {isStatic: true}
),
];
const bodies = [...walls, ...balls.map(e => e.body)];
Matter.Composite.add(engine.world, bodies);
let checkbox;
function setup() {
checkbox = createCheckbox("running?", true);
checkbox.position(0, 0);
createCanvas(500, 200);
noStroke();
}
function draw() {
if (!checkbox.checked()) { // check for pause
return;
}
background(30, 30, 30, 60);
balls.forEach(e => e.draw());
fill(160);
for (const e of walls) {
beginShape();
e.vertices.forEach(({x, y}) => vertex(x, y));
endShape();
}
if (random(2)) {
Matter.Body.applyForce(
balls[~~random(balls.length)].body,
{
x: random(0, 500),
y: random(0, 200),
},
{
x: random(-3, 3),
y: random(-3, 3),
}
);
}
Matter.Engine.update(engine);
}
body {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>
If you want to use the mouse to pause/unpause, replace the checkbox with
let running = true;
function mousePressed() {
running = !running;
}
and test running in the draw function:
function draw() {
if (!running) { // check for pause
return;
}
// ... rerender ...
}
I'm not sure if your code is exactly as it is in your project, but as shown in the original post, the second function will overwrite the first if they're in the same scope.
Another option in p5 is calling noLoop() to pause the draw loop and loop() to resume it. You can use isLooping() to figure out which to call.
If you're using the built-in renderer, create a Runner and use runner.enabled to toggle pause. According to the docs for Matter.runner.stop():
If you wish to only temporarily pause the engine, see engine.enabled instead.
...although I believe engine.enabled is a typo referring to runner.enabled. I have a PR that fixes this mistake.
See this answer for an example of pausing the internal renderer.