2

I'm trying to add mouse controls to the starter Matter.js example of two boxes. I seem to be missing something because it won't work. I just want to be able to move the bodies around with the mouse.

I'm trying to add mouse controls to the starter Matter.js example of two boxes. I seem to be missing something because it won't work. I just want to be able to move the bodies around with the mouse.

'''

<canvas id="canvasM" data-pixel-ratio="2" style="position:relative; z-index:0;"></canvas>
<script>
      // module aliases
  var Engine = Matter.Engine,
      Render = Matter.Render,
      Runner = Matter.Runner,
      Bodies = Matter.Bodies,
      Composite = Matter.Composite;
      World = Matter.World;
  
  var mouse;
  
  // create an engine
  var engine = Engine.create();
    world = engine.world;
  
  var w = window.innerWidth;
  var h = window.innerHeight;
  
  // create two boxes and a ground
  var boxA = Bodies.rectangle(.5*w+30, .7*h, 80, 80);
  var boxB = Bodies.rectangle(.5*w+60, 50, 80, 80);
  var ground = Bodies.rectangle(.5*w-1, .888*h+.05*h-30+1.5, w, .1*h, { isStatic: true });

  // add all of the bodies to the world
  Composite.add(engine.world, 
                [boxA, boxB, ground]);
  

  // create runner
  var runner = Runner.create();

  // run the engine
  Runner.run(runner, engine);
  
  var canvas = document.getElementById('canvasM');
  context = canvas.getContext('2d');
  canvas.width = window.innerWidth-130;
  canvas.height = 0.888*window.innerHeight;


  
  (function render() {
      var bodies = Composite.allBodies(engine.world);

      window.requestAnimationFrame(render);

      context.beginPath();

      for (var i = 0; i < bodies.length; i += 1) {
          var vertices = bodies[i].vertices;

          context.moveTo(vertices[0].x, vertices[0].y);

          for (var j = 1; j < vertices.length; j += 1) {
              context.lineTo(vertices[j].x, vertices[j].y);
          }

          context.lineTo(vertices[0].x, vertices[0].y);
      }

      context.lineWidth = 3;
      context.fill = '#fff';
      context.strokeStyle = '#000';
      context.stroke();
    

  var mouseC = Matter.MouseConstraint;
  mouseC.pixelRatio = 2;
  var canvmouse = Matter.Mouse.create(document.getElementById('canvasM'));
  mouseC = mouseC.create(engine,{
        mouse: canvmouse});

  Composite.add(world, mouseC);

  render.mouse = mouse;
  
  })();
  
  
  
</script>

'''

oak
  • 21
  • 3
  • It doesn't make sense to add a mouse constraint 60 times a second in the rendering loop. Do it one time up front as shown in [Using Matter.js to render to the DOM or React](https://stackoverflow.com/questions/63906218/using-matter-js-to-render-to-the-dom-or-react/65354225#65354225). – ggorlen Oct 14 '22 at 00:17
  • I did that because nothing else was working, tried copying the mouse from the example you posted still nothing – oak Oct 14 '22 at 21:49
  • Yes, your setup here has multiple fundamental issues. For example, it's unusual to call `requestAnimationFrame` when you're also using MJS's internal runner, which does the exact same thing for you. From [the docs](https://brm.io/matter-js/docs/classes/Runner.html): "If you are using your own game loop instead, then you do not need the `Matter.Runner` module." It's unclear whether you mean to use your own loop or MJS's. Which do you want? I suggest using the linked example as a skeleton for building your app rather than the other way around. It works, so you can adapt it to fit your use case. – ggorlen Oct 14 '22 at 21:50
  • found the error it was something with my variable naming. (your reply did help me with my focus) Now the coordinate system seems to be off and I've accounted for the pixel ratio but after that, I've got it. – oak Oct 14 '22 at 22:17
  • Great. Feel free to post a [self answer](https://stackoverflow.com/help/self-answer) if you've solved your problem, but I'd caution against this design without a good deal more thought, even if it appears to work (you may still be running two event loops simultaneously, which is harmful to performance at best, and prone to bugs at worst). – ggorlen Oct 14 '22 at 22:19
  • I will post a self-answer when I've got the coordinate system down (any thoughts?). Right now I am only interacting with shapes from about an inch away. Also don't know how to implement one loop as you've said because when I take one away the other stops working. – oak Oct 14 '22 at 22:28
  • There are two options: use the MJS runner loop or your own loop, but not both. I'm just going to keep referring you back to the working example above, both for coordinates and for the rendering loop. If you use your loop, call `engine.update()` inside the RAF loop as I show in the example. – ggorlen Oct 14 '22 at 22:40
  • 1
    Oh, sorry--you're using canvas, not the DOM, but the conept is pretty much the same. https://codepen.io/ggorlen/full/LOwrxX is an example with canvas, mouse and correct coordinates. I can't seem to find any canvas examples of mine on SO so I'll add an answer if I have time. – ggorlen Oct 14 '22 at 22:53
  • I can't seem to see how your mouse coordinates are basically baked in and mine is off. – oak Oct 14 '22 at 23:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248816/discussion-between-oak-and-ggorlen). – oak Oct 14 '22 at 23:23

2 Answers2

0

As mentioned in the comments, creating a Matter runner and using your own update loop are mutually exclusive. If you do wish to use a MJS runner, hook into the update events and redraw your UI as necessary. If you prefer to use your own update loop, skip the renderer and call Matter.update() in the loop.

Here's an example of using your own loop:

const w = window.innerWidth;
const h = window.innerHeight;
const engine = Matter.Engine.create();

const boxA = Matter.Bodies.rectangle(
  0.5 * w + 30, // x
  0.7 * h, // y
  80, // w
  80, // h
);
const boxB = Matter.Bodies.rectangle(
  0.5 * w + 60,
  50,
  80,
  80
);
const ground = Matter.Bodies.rectangle(
  0.5 * w - 1,
  0.888 * h + 0.05 * h - 30 + 1.5,
  w,
  0.1 * h,
  {
    isStatic: true,
  }
);

const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
canvas.width = w - 130;
canvas.height = 0.888 * h;

const mouseConstraint = Matter.MouseConstraint.create(
  engine,
  {element: canvas}
);
Matter.Composite.add(engine.world, [
  boxA,
  boxB,
  ground,
  mouseConstraint,
]);
const bodies = Matter.Composite.allBodies(engine.world);

(function render() {
  window.requestAnimationFrame(render);

  context.clearRect(0, 0, canvas.width, canvas.height);
  context.beginPath();

  for (const {vertices} of bodies) {
    context.moveTo(vertices[0].x, vertices[0].y);
    vertices.forEach(({x, y}) => context.lineTo(x, y));
    context.lineTo(vertices[0].x, vertices[0].y);
  }

  context.lineWidth = 3;
  context.fill = "#fff";
  context.strokeStyle = "#000";
  context.stroke();

  Matter.Engine.update(engine);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
<canvas></canvas>
ggorlen
  • 44,755
  • 7
  • 76
  • 106
0

The following is a fix to my code. See the variable named mouseControl and in particular Matter.Mouse.setScale(canvmouse,{x:2,y:2}); to fix the mouse coordinate scaling. Special thanks to @ggorlen for the focus and informing of the loop redundancy.


<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>

<canvas id="canvasM" data-pixel-ratio="2" style="position:relative; z-index:0;"></canvas>
<script>
      // module aliases
  var Engine = Matter.Engine,
      Render = Matter.Render,
      Runner = Matter.Runner,
      Bodies = Matter.Bodies,
      Composite = Matter.Composite;
      World = Matter.World;
  
  var mouse;
  
  // create an engine
  var engine = Engine.create();
    world = engine.world;
  
  var w = window.innerWidth;
  var h = window.innerHeight;
  
  // create two boxes and a ground
  var boxA = Bodies.rectangle(.5*w+30, .7*h, 80, 80);
  var boxB = Bodies.rectangle(.5*w+60, 50, 80, 80);
  var ground = Bodies.rectangle(.5*w-1, .888*h+.05*h-30+1.5, w, .1*h, { isStatic: true });

  // add all of the bodies to the world
  Composite.add(engine.world, 
                [boxA, boxB, ground]);
  

  
  var canvas = document.getElementById('canvasM');
  context = canvas.getContext('2d');
  canvas.width = window.innerWidth-130;
  canvas.height = 0.888*window.innerHeight;


  
  (function render() {
      var bodies = Composite.allBodies(engine.world);

      window.requestAnimationFrame(render);

      context.beginPath();

      for (var i = 0; i < bodies.length; i += 1) {
          var vertices = bodies[i].vertices;

          context.moveTo(vertices[0].x, vertices[0].y);

          for (var j = 1; j < vertices.length; j += 1) {
              context.lineTo(vertices[j].x, vertices[j].y);
          }

          context.lineTo(vertices[0].x, vertices[0].y);
      }

      context.lineWidth = 3;
      context.fill = '#fff';
      context.strokeStyle = '#000';
      context.stroke();
    
  Matter.Engine.update(engine);
  })();
  
  var mouseC = Matter.MouseConstraint;
  var canvmouse = Matter.Mouse.create(document.getElementById('canvasM'));
  Matter.Mouse.setScale(canvmouse,{x:2,y:2});

  mouseControl = mouseC.create(engine,{
        mouse: canvmouse});

  Composite.add(world, mouseControl);

  render.mouse = mouse;
  
  
</script>


oak
  • 21
  • 3