liabru / matter-js

a 2D rigid body physics engine for the web ▲● ■
MIT License
16.26k stars 1.95k forks source link

Force velocity problem sliding across multiple rectangles #970

Open wnr opened 3 years ago

wnr commented 3 years ago

Hi,

I have a rectangle with { inertia: 'Infinity', mass: 100, frictionAir: 0, friction: 0, } that I wish to slide horizontally on top of a plane of tiles made out of multiple rectangles (classic tilemap). The tiles are setup like so:

    var lstack = Composites.stack(50, 100, 15, 10, 0, 0, function (x, y) {
      return Bodies.rectangle(x, y, 30, 50, {
        isStatic: true,
        render: {
          fillStyle: 'red',
          strokeStyle: 'black'
        }
      });
    });

I apply some force to the sliding rectangle Matter.Body.applyForce(player, player.position, Matter.Vector.create(0.05, 0).

Sometimes the simulation works as expected, but sometimes the rectangle simply stops at a seam/joint between two tiles. Please see attached video. Im reloading the script in order to restart the simulation in the video.

https://user-images.githubusercontent.com/719555/107157921-26f4a200-6987-11eb-91c7-4e126009e132.mov

What's going on here?

Here's the full source code:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Physics</title>
</head>

<body>
  <script type="module">
    import Matter from "matter-js";
    var Engine = Matter.Engine,
      Render = Matter.Render,
      Runner = Matter.Runner,
      Composites = Matter.Composites,
      MouseConstraint = Matter.MouseConstraint,
      Mouse = Matter.Mouse,
      World = Matter.World,
      Constraint = Matter.Constraint,
      Bodies = Matter.Bodies;

    // create engine
    var engine = Engine.create(), world = engine.world;

    // create renderer
    var render = Render.create({
      element: document.body,
      engine: engine,
      options: {
        width: 800,
        height: 400,
        wireframes: true,
        showVelocity: true
      }
    });

    Engine.run(engine);

    Render.run(render);

    var player = Bodies.rectangle(300, 70, 20, 20, {
      inertia: 'Infinity',
      mass: 100,
      frictionAir: 0,
      friction: 0,
      render: {
        fillStyle: 'blue',
        strokeStyle: 'black'
      }
    });

    var lstack = Composites.stack(50, 100, 15, 10, 0, 0, function (x, y) {
      return Bodies.rectangle(x, y, 30, 50, {
        isStatic: true,
        render: {
          fillStyle: 'red',
          strokeStyle: 'black'
        }
      });
    });

    World.add(world, [
      lstack,
      player,
      // walls
      Bodies.rectangle(400, 0, 810, 30, { isStatic: true }),
      Bodies.rectangle(400, 400, 810, 30, { isStatic: true }),
      Bodies.rectangle(800, 200, 30, 420, { isStatic: true }),
      Bodies.rectangle(0, 200, 30, 420, { isStatic: true })
    ]);

    setTimeout(() => {
      setInterval(() => {
        Matter.Body.applyForce(player, player.position, Matter.Vector.create(0.05, 0))
      }, 10)
    }, 300)
  </script>
</body>

</html>
michael-garofalo commented 3 years ago

What's going on here?

It looks like the sliding rectangle is getting caught on one of the tiles. Why that is happening? I don't know. However, here are some things you could try. It might isolate the problem.

First, maybe set the slop to zero... https://brm.io/matter-js/docs/classes/Body.html#property_slop

A Number that specifies a tolerance on how far a body is allowed to 'sink' or rotate into other bodies. Avoid changing this value unless you understand the purpose of slop in physics engines. The default should generally suffice, although very large bodies may require larger values for stable stacking.

...or maybe make it higher than the default of 0.05. 🤷🏻‍♂️

Next, maybe try adding some Chamfering (Rounded Corners)... https://brm.io/matter-js/demo/#rounded

Perhaps the corners are too sharp, so they get caught on each other.

Expanding on that idea, does it happen with a circle? If not, then maybe treat the circle as the collision box, something like a hit-box, and then just layer the graphics on-top of the circle. I've done something similar for collision detection.

https://forums.tumult.com/t/physics-mini-templates-for-tumult-hype-4/11508/27

The little red dots next to the ball are for collision detection. By keeping the sensors next to the ball, I can do things like wall jumping. So, instead of keeping the sensors at a fixed distance, perhaps it's the graphics that need to be kept on-top (as a separate element) of the main Physics element.

wnr commented 3 years ago

Thanks for you reply!

The slop doesn't seem to change anything in this situation. The chamfering prevented it from getting stuck, but only when big enough to make the object wobble up and down as it goes along. Yeah the circle also prevents it from getting stuck, but the circle sometimes "pop up" a bit when it goes over a seam/joint.

Do people convert adjacent objects to a single object in these cases?

michael-garofalo commented 3 years ago

Do people convert adjacent objects to a single object in these cases?

I was thinking about that when I first saw your project. Why not just use a straight line if you want a straight line? You could layer it right over the tiles.

I suppose it depends on the game engine and the game you want to make. It looks like you want to use tiles. You have to weigh the advantages and disadvantages of Matter.js in that situation, as you do have choices — such as Stencyl or the other physics engines in Phaser.js.

What's nice about Matter.js is that you can make custom polygons. You could also use springs/constraints. It depends what's your main character. If it's a car... https://brm.io/matter-js/demo/#car ...maybe there wouldn't be so much shifting over the bumps. That's a lot of work to test and it still might not be as seamless. You have to be fussy about this stuff because players will notice.

About 10 years ago, I was working on a game called "BOT", using GameSalad... https://photics.com/bot/ ...and I used MANY invisible custom Physics shapes over background images. It created the effect I was looking for, but it was very time consuming. Looking back, was it a waste of time? (BOT did not have great sales.) What I learned was that game development is quite time consuming. Did I choose the wrong technique? Surprisingly, I think that ArenaNet was doing something similar with Guild Wars — a 3D game. There were invisible walls around the edge, so the player can't leave the area/fall through the world. Someone had to make that. Invisible walls are common in video game development. (Heh, and hacking such walls is common in speed running.)

Here, you just want to make sure your straightways are really straight. You could just make a really long rectangle in those cases, then you wouldn't have to use so many Physics objects. If I was building a tile-based game, with simple 8-BIT era Physics, I might not use Matter.js. Tiles are from the early days of gaming, where the game consoles weren't very powerful. They had to use tricks to save space and processing power.

Game performance is still an issue today. As an example, Matter.js / Hype is great with creating custom shapes. Here's the Matter.js demo... https://brm.io/matter-js/demo/#svg ...and there's one here too... https://photics.com/memoirs-of-a-beta-tester-tumult-hype-4-review/ ...after I made that, I was amazed how the star shape could interact with the clover shape. Yet, those are still fairly simple shapes. I don't know how well it would do in a full game with many custom shapes... lots of graphics... parallax scrolling... sound effects... etc.

Here's another example... https://photics.com/free-template-tuesday-33-tumult-hype-jigsaw/ ...moving one piece around, no problem. But, I decided to get a little goofy and make the pieces explode apart on startup. It looks cool, but it's a massive strain on the CPU and can be quite laggy on mobile devices. Why do I mention this? Theoretically you could create custom polygons to outline your entire scenes, but that just means Matter.js is just going to convert those shapes to simpler polygons. (You can see this in debug mode — showInternalEdges) I don't know what that will do to performance.

So, you have a tough decision, as there are a few issues...

In general, to improve performance, you want to keep the number of Physics shapes down. So, perhaps the real solution here is to create a mix of graphics, invisible (but simple) Physics objects, some visible Physics objects, and maybe even a few complex Physics objects. (That last one is really important, as it helps to determine if Matter.js is right for you.)

As for working with tiles, that was one of my design questions with BOT. I was wondering if I should use tiles. I think that could have given the game a better look, but there wasn't enough time to switch directions. It was a tough spot. Game development can be deceptively difficult.

liabru commented 3 years ago

@wnr could you try overlapping those rectangles? I think there is a known issue, I'll try investigate further.

In any case though, it's strongly recommended that you merge static bodies that are touching for stability and performance reasons.

@michael-garofalo yeah it's very much true that a physics engine has very different properties and tradeoffs to e.g. a dedicated RPG or platformer engine. It's also possible to combine both approaches as seen in the Manipulation demo.

wnr commented 3 years ago

@liabru I just tried overlapping the tiles, but it didn't change much. Yeah merging seems the way to go for performance, but I hoped this would work for a naive approach during prototyping.

liabru commented 3 years ago

You could also try rounding the corners of the sliding body or maybe even using a circle instead, which should help move over bumps.

Also if you're trying to make something directly controllable like a player character, then avoid using forces and take a look at the Manipulation example for more reliable approaches.

liabru commented 2 years ago

I'm not sure how much the recent fixes are related to this but I'd be interested to know if the latest release 0.18.0 helps anyone seeing this kind of issue.