I have created a pause button for a game called Pirate Invasion. I'm trying to pause/freeze bodies at their place and resume them from the position that they are in.
I have used setStatic
and isStatic
using the function mousePressed()
. However, they both show an error Uncaught TypeError: Matter.Bodies.setStatic is not a function
.
Am I missing out on something or is this got to do with saving the positions or something similar?
Boats are the bodies that have to "pause/freeze".
function mousePressed(playButton) { // this is my play button which works perfectly
gameState = "play"
World.remove(world, playButton)
World.remove(world, playImage)
}
function mousePressed(pauseButton) { // pause button which gives an error
Matter.Bodies.setStatic(boats, true)
}
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.20.0/matter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/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.