Keyboard movement is jerky in matter.js
Asked Answered
H

1

6

I'm new to matter.js and I'm using Body.applyForce to move my box in a platformer I'm creating. I was wondering if there was an easier function to use that allows constant movement instead of just randomly going from slow to fast.

Here's all my code:

var Engine = Matter.Engine,
    Render = Matter.Render,
    Runner = Matter.Runner,
    Bodies = Matter.Bodies,
    Body = Matter.Body,
    World = Matter.World,
    Composite = Matter.Composite;
var engine = Engine.create();
var render = Render.create({
    element: document.body,
    engine: engine
});

var boxA = Bodies.rectangle(400, 200, 80, 80);
var boxB = Bodies.rectangle(600, 560, 80, 80);
var ground = Bodies.rectangle(435, 630, 810, 60, {
    isStatic: true
});
var leftWall = Bodies.rectangle(0, 200, 60, 800, {
    isStatic: true
});

Body.setStatic(boxB, true)
Body.setInertia(boxA, Infinity)

const cases = Object.assign(Object.create(null), {
    KeyD: (Body, boxA) => {
        Body.applyForce(boxA, {
            x: boxA.position.x,
            y: boxA.position.y
        }, {
            x: 0.03,
            y: 0,
        })
    },

    KeyA: (Body, boxA) => {
        Body.applyForce(boxA, {
            x: boxA.position.x,
            y: boxA.position.y
        }, {
            x: -0.03,
            y: 0,
        })
    },
})

document.addEventListener("keydown", event => {
    cases[event.code]?.(Body, boxA)
})

Composite.add(engine.world, [boxA, boxB, ground, leftWall]);
Render.run(render);
var runner = Runner.create();
Runner.run(runner, engine);
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>
Humboldt answered 17/7, 2021 at 1:36 Comment(0)
S
5

The problem is that there's no layer of indirection between the key handler and the Matter.js event loop as described agnostic of MJS in Remove key press delay in Javascript. This leaves you to the whims of the operating system key retrigger mechanism.

As the linked post describes in detail, the solution is to only use the keydown and keyup handlers to add and remove key codes from a set of currently-pressed keys, and nothing else. You'll only take action on bodies in the normal flow of the MJS event loop, which involves adding a handler to fire on beforeUpdate events.

Here's the idea applied to your example. Note that I added friction to boxA (the player box). Tweaking the force you're applying to it along with friction and other settings will be needed to make the game feel right for you.

const engine = Matter.Engine.create();
const runner = Matter.Runner.create();
const render = Matter.Render.create({
  element: document.body,
  engine: engine
});

const boxA = Matter.Bodies.rectangle(400, 200, 80, 80, {
  inertia: Infinity,
  friction: 0.1,
});
const boxB = Matter.Bodies.rectangle(600, 560, 80, 80, {
  isStatic: true,
});
const ground = Matter.Bodies.rectangle(435, 630, 810, 60, {
  isStatic: true
});
const leftWall = Matter.Bodies.rectangle(0, 200, 60, 800, {
  isStatic: true
});

const keyHandlers = {
  KeyD: () => {
    Matter.Body.applyForce(boxA, {
      x: boxA.position.x,
      y: boxA.position.y
    }, {x: 0.02, y: 0})
  },
  KeyA: () => {
    Matter.Body.applyForce(boxA, {
      x: boxA.position.x,
      y: boxA.position.y
    }, {x: -0.02, y: 0})
  },
};

const keysDown = new Set();
document.addEventListener("keydown", event => {
  keysDown.add(event.code);
});
document.addEventListener("keyup", event => {
  keysDown.delete(event.code);
});

Matter.Events.on(engine, "beforeUpdate", event => {
  [...keysDown].forEach(k => {
    keyHandlers[k]?.();
  });
});

Matter.Composite.add(
  engine.world, [boxA, boxB, ground, leftWall]
);
Matter.Render.run(render);
Matter.Runner.run(runner, engine);
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.min.js"></script>
Sister answered 18/7, 2021 at 0:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.