Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent object passing through walls / tunneling (implement CCD) #5

Open
liabru opened this issue Feb 27, 2014 · 86 comments
Open

Prevent object passing through walls / tunneling (implement CCD) #5

liabru opened this issue Feb 27, 2014 · 86 comments

Comments

@liabru
Copy link
Owner

liabru commented Feb 27, 2014


If you need fast moving or thin bodies a simple and robust solution is to sub-step using multiple updates per frame:

See the example here: #5 (comment)

As well as sub-steps also consider:

  • make thin bodies thicker (but can render them thinner similar to this example)
  • apply a max speed limit to bodies (e.g. similar to this example)
  • avoid large timesteps (<= 16.666ms is recommended)
  • ray cast along body velocity and clamp velocity to e.g. 50%

Currently there is no continuous collision detection (CCD), so fast moving objects pass through other objects.

A description of the problem:
http://www.stencyl.com/help/view/continuous-collision-detection

Solution is to implement a CCD algorithm.

One I'm currently considering is speculative contacts, see here and here.

"we compute the closest distance d between the two objects; the idea is that we want to remove exactly the right amount of velocity from A such that it will be exactly in touching contact with B on the next frame"

I've already implemented the code that extends the AABB based on object velocity.

The next part is to find the closest distance between two objects and remove the required velocity.

@abataille
Copy link

I just added a poor man's solution to the tunneling problem. It detects if a body is outside the world bounds and then reverses the velocity and translates the body back. I tried via an event. Here is the code. Please have a look.
Events.on(_engine, 'afterTick afterRender', function (event)
{
// avoid tunneling
for (var i = 0; i < _world.bodies.length; i++)
{
var bodyA = _world.bodies[i];
if (bodyA.isStatic || bodyA.isSleeping)
{
continue;
}
var outside = false;
var adjustX = 0;
var adjustY = 0;
var epsilon = 0.01;
if (bodyA.bounds.min.y + epsilon >= _world.bounds.max.y)
{ // bottom
adjustY = Math.abs(bodyA.bounds.max.y - _world.bounds.max.y);
Body.translate(bodyA, {
x: 0,
y: -adjustY
});
bodyA.velocity.y = -1.0;
outside = true;
}
if (bodyA.bounds.max.x - epsilon<= _world.bounds.min.x)
{ // left
bodyA.velocity.x = 1.0;
adjustX = Math.abs(bodyA.bounds.min.x - _world.bounds.min.x);
Body.translate(bodyA, {
x: adjustX,
y: 0
});
outside = true;
}
if (bodyA.bounds.max.y - epsilon <= _world.bounds.min.y)
{ // Top
adjustY = Math.abs(bodyA.bounds.min.y - _world.bounds.min.y);
Body.translate(bodyA, {
x: 0,
y: adjustY // since body.bounds.min.y is negative
});
bodyA.velocity.y = 1.0;
outside = true;
}
if (bodyA.bounds.min.x + epsilon > _world.bounds.max.x)
{ // right
bodyA.velocity.x = -1.0;adjustX = Math.abs(bodyA.bounds.max.x - _world.bounds.max.x);
Body.translate(bodyA, {
x: -adjustX,
y: 0
});
outside = true;
}
if (outside)
{
Bounds.update(bodyA.bounds, bodyA.vertices, bodyA.velocity);
}
}
});

@liabru
Copy link
Owner Author

liabru commented Mar 14, 2014

Thanks for sharing, but does this work?

I ask because body.velocity is technically read only - the engine uses position Verlet meaning velocity is implicit to changes in position (see here)!

So what you need to do instead is simply set the body position to be inside the world, the engine will handle the velocity change for you.

Either way, as you say this is indeed a poor mans solution, as it's only a solution to going outside of the world bounds.

BTW note the event name is not 'afterTick afterRender', those are two separate events (that happen to relate to the same point in the step). You only need to use one here (e.g. it's like jQuery's events), otherwise this will run twice per step.

What we really need implementing is speculative contacts. I'll get round to it at some point hopefully!

@abataille
Copy link

You are right. velocity change is not necessary. Just the translation does it.
I know, it does not work for inside objects, but at least it leaves the bodies at the table.

@liabru liabru self-assigned this Apr 22, 2014
@liabru liabru removed the bug label May 6, 2014
@shakiba
Copy link

shakiba commented May 11, 2014

+1

@liabru liabru removed their assignment May 11, 2014
@Danetag
Copy link

Danetag commented Nov 11, 2014

Still experiencing this issue, I guess it's still unsolved?

@liabru
Copy link
Owner Author

liabru commented Nov 11, 2014

It's a pretty big feature to implement really, I've played around with some ideas for this but I've not yet got much working. It's a tricky one.

I'm considering implementing a simple version for now (ray casting) that should prevent some of the more obvious tunneling cases.

@Danetag
Copy link

Danetag commented Nov 12, 2014

Ok I totally understand, thank you so much for all your hard work :)

@anjiro
Copy link

anjiro commented Feb 16, 2015

Note that increasing engine.timing.timeScale can cause this issue as well.

@isaac-jordan
Copy link

Still having this issue using latest master as of e698b6b. I have somewhat mitigated it by halving the engine.timing.timeScale value. Not sure of the repercussions of this, but works for my simple use case.

@liabru liabru mentioned this issue Jan 9, 2016
@homerjam
Copy link

+1

@slaskis
Copy link

slaskis commented Jan 28, 2016

@liabru for https://cubeslam.com I ported a few SAT implementations to javascript but finally fell for this brilliant SAT implementation and converted it to be a part of this library.

It's very similar to your implementation but will also project ahead based on the polygons relative velocity to see if it will intersect and then returns a minimum translation vector which may be applied to keep two polygons from colliding if desired.

I found it to be quite stable and fast even in the pace of cube slam. Unfortunately google code just shut down so the implementation code for it is not viewable online anymore. But you can download it and have a look at the narrowphase implementation in lib/sim/physics.js#L74.

@liabru
Copy link
Owner Author

liabru commented Jan 28, 2016

@slaskis I remember playing with cubeslam, awesome project!

Thanks for the info. I actually played with using SAT for continuous collisions but I couldn't get it working at the time, so I'll take a look at your implementation for some pointers.

@slaskis
Copy link

slaskis commented Jan 28, 2016

@liabru thanks! I hope it can help you get this sweet library even better

@dcrockwell
Copy link

+1 Seeing lots of tunneling with bodies passing through svg generated bodies.

@ser10us
Copy link

ser10us commented Mar 28, 2016

Is there any workaround for this issue? The proposal from @abataille doesn't work for me.

@liabru
Copy link
Owner Author

liabru commented Mar 28, 2016

Not yet, I have a work in progress implementation. Depending on your needs, you could try doing some ray casts between updates (e.g. between centroids of bodies moving over a certain speed), if there's an intersection then move the body back. This won't be perfect but might help for some cases.

@slaskis
Copy link

slaskis commented May 11, 2016

@liabru would you mind sharing your in progress implementation as a PR or branch? I'm curious what it looks like and maybe we can help out?

@IceReaper
Copy link

Will this also allow collision with simple lines? To be more specific imagine a force field, which limits you to not pass from both sides but has no thickness.
Which comes to the second idea of force fields coliding only in one direction, so you might move into the forcefield, but not leave it again.
Imagine it as a simple polygon which has only a one-sided face, you walk through it as you cannot see it. If you try to move back you see a wall there.
Used that feature to build some nasty tricky puzzles in prey a while ago, where the back-side was a portal placed inside a door. Which results in a door bringing you from a to b, but the same door back brings you from b to c :D

@liabru
Copy link
Owner Author

liabru commented May 14, 2016

@slaskis it is in a very unstable state, I was trying a different approach than using SAT. But it looks like it's not going to be viable, so I'm going to try implement it again in the same way you did. Looking at your code, the key part seems to be these lines right?

@liabru
Copy link
Owner Author

liabru commented May 14, 2016

@IceReaper if the implementation was perfect, then yes you could do a super thin body and it should never let anything through. I'm not sure if a practical implementation will be that accurate though, but hopefully.

Either way, for the kind of game mechanics you are talking about, it is better for you to implement that logic directly as it's not really in the scope of a physics engine but rather a game engine. Though once plugins are implemented (#213) you could use them to integrate this.

@jtoy
Copy link

jtoy commented May 14, 2020

+1

@huy-nguyen
Copy link

huy-nguyen commented Jun 3, 2020

I ran into this tunneling problem with matter.js and eventually abandoned it in favor of box2d.js and haven't had a problem since. box2d.js's API is less nice because it's directly compiled from C++ using Emscripten but it wasn't hard to switch. Highly recommended.

@Loque18
Copy link

Loque18 commented Jun 13, 2021

it's already 2021, we are almost in the middle of the year, do we have any progress with CDD ?

@WesWedding
Copy link

Be the change in the world you wish to be, peeps! Any Pull Requests on this issue?

@dzcpy
Copy link

dzcpy commented Jun 22, 2021

No, switching to box2d is the right move

@mulhoon
Copy link

mulhoon commented Dec 16, 2021

There seemed to be a hack on Stack Overflow that worked with 0.12.0 and earlier.

@bombzj
Copy link

bombzj commented Feb 14, 2022

No, switching to box2d is the right move

Tried and failed to solve the issue. The problem is critical. I don't know if I should wait or jump ship. The tools and docs in matter.js are so much better than box2d.js.

@dzcpy
Copy link

dzcpy commented Feb 14, 2022

No, switching to box2d is the right move

Tried and failed to solve the issue. The problem is critical. I don't know if I should wait or jump ship. The tools and docs in matter.js are so much better than box2d.js.

For me box2d works perfectly in my projects. I used Planck.js though. It's true that matter.js's documentation is way better, but I can't get it work

@joshkpeterson
Copy link

joshkpeterson commented Feb 14, 2022 via email

@stefannew
Copy link

No, switching to box2d is the right move

Tried and failed to solve the issue. The problem is critical. I don't know if I should wait or jump ship. The tools and docs in matter.js are so much better than box2d.js.

You could open a PR instead of waiting.

@liabru
Copy link
Owner Author

liabru commented Feb 25, 2022

The simplest solution if you need fast moving or thin bodies is to sub-step, it's robust and not all that expensive now and you get higher quality results all round.

You can even dynamically increase your substeps for just a few frames when you detect fast moving bodies, if performance was an issue (and you don't mind possibly inconsistent results, like with a dynamic timestep).

Until I've finished adding substeps as a feature into Matter.Runner (it's a bit tricky to handle all possible framerates, plus a few other things for the general case), here is a very simplified example (that assumes fixed 60fps) to demonstrate:

const delta = 1000 / 60;
const subSteps = 3;
const subDelta = delta / subSteps;

(function run() {
    window.requestAnimationFrame(run);
    for (let i = 0; i < subSteps; i += 1) {
      Engine.update(engine, subDelta);
    }
})();

As well as sub-steps also consider:

  • make thin bodies thicker (but can render them thinner similar to this example)
  • apply a max speed limit to bodies (e.g. similar to this example)
  • avoid large timesteps (<= 16.666ms is recommended)
  • ray cast along body velocity and clamp velocity to e.g. 50%

Let me know if that helps - I realise this isn't so obvious unless you're very used to game dev!

@hossein4bedi
Copy link

Same Issue while working with Matter.js in React Native!
Changing the body size and velocity or obstacle thickness does not solve the issue.

@nightness
Copy link

nightness commented Jun 23, 2022

I switched from using applyForce to setVelocity, fixed the tunneling bug in my app.

        Matter.Body.setVelocity(ball, { x: ball.velocity.x + 20, y: ball.velocity.y - 20 });
        // Matter.Body.applyForce(ball, ball.position, { x: 0.05, y: -0.05 });

@Anatoly03
Copy link

Anatoly03 commented Jul 14, 2022

Is there some sort of path tracing collision detection? Say if object A moves from position X to position Y, create a line (X1, X2, Y1, Y2) and check if the line intersects with the object lines? My body seems to be phasing through other bodies despite the speed not being that quick.

Or to be precise, when applying force to a body near to another body it phases through, maybe I have another problem?

@liabru
Copy link
Owner Author

liabru commented Jul 19, 2022

@Anatoly03 see the last point in #5 (comment) about raycasting - to avoid some of the worst cases that approach seems reasonable (especially for user controlled bodies), but generally difficult to make robust. Again I'd strongly recommend using sub-stepping as mentioned in that comment.

@Anatoly03
Copy link

@Anatoly03 see the last point in #5 (comment) about raycasting - to avoid some of the worst cases that approach seems reasonable (especially for user controlled bodies), but generally difficult to make robust. Again I'd strongly recommend using sub-stepping as mentioned in that comment.

image

Is it possible to creating an extended polygon of the movement by tracing the nodes, then check for objects intersecting this polygon?

@jonlepage
Copy link

jonlepage commented Dec 25, 2022

ho am sad, i study the engine full day and i just realize there a big bug in math of this libs !
actuality most of my entities bypass walls ! and seem not easy solution !
why not add a flag like

Engine.create({
		ccdLevel: 1, //force to apply to limited by ccd
	}

@OdinvonDoom
Copy link

The simplest solution if you need fast moving or thin bodies is to sub-step, it's robust and not all that expensive now and you get higher quality results all round.

You can even dynamically increase your substeps for just a few frames when you detect fast moving bodies, if performance was an issue (and you don't mind possibly inconsistent results, like with a dynamic timestep).

Until I've finished adding substeps as a feature into Matter.Runner (it's a bit tricky to handle all possible framerates, plus a few other things for the general case), here is a very simplified example (that assumes fixed 60fps) to demonstrate:

const delta = 1000 / 60;
const subSteps = 3;
const subDelta = delta / subSteps;

(function run() {
    window.requestAnimationFrame(run);
    for (let i = 0; i < subSteps; i += 1) {
      Engine.update(engine, subDelta);
    }
})();

As well as sub-steps also consider:

  • make thin bodies thicker (but can render them thinner similar to this example)
  • apply a max speed limit to bodies (e.g. similar to this example)
  • avoid large timesteps (<= 16.666ms is recommended)
  • ray cast along body velocity and clamp velocity to e.g. 50%

Let me know if that helps - I realise this isn't so obvious unless you're very used to game dev!

Trying to substep like this breaks the outcome by moving the bodies proportional to the number of steps. (If I use 3 substeps I move 3 times as fast.) I think this is maybe because in the body update, I noticed that the time delta doesn't directly influence the velocity, only the acceleration ((body.force.x / body.mass) * deltaTimeSquared), so I tried compensating for this by modifying the "correction" value (which does seem to directly influence the velocity), not used in the sample above, but that just gives me various other problems, like only getting to tiny burst of movement before stopping cold.

Is there a way to use any number of substeps and be sure to see the same position step for one's matter bodies as if no substeps were used at all?

@Beknur2002
Copy link

+1

@liabru
Copy link
Owner Author

liabru commented Nov 12, 2023

PR #1254 adds built-in support for sub-stepping (as discussed above) - see the included Example.substep on that branch for an example if you wish to try it out.

@liabru
Copy link
Owner Author

liabru commented Jun 24, 2024

As of 0.20.0 Matter.Runner supports sub-stepping fixed timesteps including on high refresh displays see #1300.

This can help with reducing bodies passing through each other in many cases. See Example.substep on how to set up Matter.Runner with a small timestep (e.g. 120hz depending on performance needs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests