Open mbchang opened 8 years ago
See #21. Basically the linear momentum is being converted to angular momentum on collision (because circles are approximated at the moment).
Hopefully my last post helped you resolve your issue, so closing this. Feel free to reply if you need more help.
I was trying something similar to the original poster. Just a ball bouncing on a static body.
var ball = Bodies.circle(100, 250, 50, { inertia: Infinity, restitution: 1, friction: 0, frictionAir: 0, frictionStatic: 0});
Even with inertia set to Infinity, the ball loses energy and bounces closer and closer to the ground, although interestingly never fully stopping. It seemingly bounces forever very close to the ground. Am I missing something?
How quickly does it lose energy? I realise conservation isn't exactly perfect at the moment, which is a bit of a problem, but hopefully it takes a while? Can you show a jsfiddle?
It loses it noticeably quick.
Thanks for the example. It's pretty quick indeed, I will investigate this. I expect it may be due to floating point errors but it does seem a little quick even for that.
Take a look at this jsfiddle:
https://jsfiddle.net/vd7d25pu/10/
Notice that the ball does appear to bounce forever here. The only difference is that the 'ground' rectangle's height is set to 1. I stumbled upon this accidentally.
I have the same problem with many balls (gravity turned off) Here is my jsfiddle example: https://jsfiddle.net/vd7d25pu/11/
I considered @wmike1987 's comment above and set 1 height to borders. However balls are still losing the force and not moving forever, they stop moving after some time. Probably an energy loss is after ball collisions.
One thing I noticed: if I increase mass of the balls (and force proportionally) then it lasts longer but still not infinite.
Thanks for the test case. My guess is that at least some of the loss is due to floating point errors (it seems that even box2d has the same issue). There's an article on energy drift which suggests the same.
My suggestion is to artificially add energy (through velocity or forces) back in over time, which you may be able to do yourself using collision or tick events. But a generic solution for any situation might be difficult.
Any idea why this is happening?
https://jsfiddle.net/vd7d25pu/7/ - loses energy https://jsfiddle.net/vd7d25pu/10/ - appears to not lose energy
Hardly any difference in the code, just the size of one of the objects.
After some testing, it seems like the radius of the circle (in my case) had an influence on how long it bounced. This is related to the mass, which I suspect is the deciding factor. My temporary workaround is to set a restitution of 1.01.
Interesting. Maybe try using Bodies.polygon with a high number of sides (like 50) to see if that helps.
I am facing the same issues. I want a very slow body (ball or rectangle does not change what I get) that bounces forever with no friction. I get a body that stops at a wall, if the initial force is very low. I've used { inertia: Infinity, inverseInertia: 0, restitution: 1, frictionStatic: 1, frictionAir: 0, friction: 0 }
Any suggestion?
in beforeUpdate event:
if(ball.speed != 0){
let speedMultiplier = 11.241098900509593/ball.speed // 11.241098900509593 == initial (starting) ball speed
Body.setVelocity(
ball,
{ x: ball.velocity.x*speedMultiplier, y: ball.velocity.y*speedMultiplier }
);
}
in beforeUpdate event:
if(ball.speed != 0){ let speedMultiplier = 11.241098900509593/ball.speed // 11.241098900509593 == initial (starting) ball speed Body.setVelocity( ball, { x: ball.velocity.x*speedMultiplier, y: ball.velocity.y*speedMultiplier } ); }
or afterCollision
Hi! Sorry for gravedigging.
This still seems to be an issue. I put multiple balls in a box (no friction, infinite inertia) and calculate the total kinetic energy over time. I initialize all of them by applyForce. There are 3 major problems. 1. Energy loss over time, 2. Sticky walls, 3. reflection angle on walls not always correct (tendency towards the wall). If the E-loss would just be a small loss per collision it would be fine, but it seems to distort the collision angle as well. In case a wall is sticky, I checked on the body.velocity x/y ratio and it was still the same (ignoring that one component was 0 since it stuck in the wall).
With the Workaround of @Menowa1709 the energy is conserved, walls are not sticky and the angle looks fine. Here is a fiddle where the fix can be switched on/off on line 77 https://jsfiddle.net/dediggefedde/3n6whja5/22/
The problem of the workaround is ignoring inertia and friction if it is switched on. also momentum transfer based on mass is not working. (In my code I made a small error setting all speeds to the first ones, but even splitting them does not fix this. One would need to calculate the e-transfer by hand)
This looks like it's still an issue, so I'm posting up what I found.
The momentum loss is quite significant. I can't just increase the bounce above 1, because then it will eventually gain too much energy. (Without CCD, the bouncing element will just fly out of the scene.)
Based on previous conversations, I tried two things to fix this problem.
You should check the
slop
property. https://github.com/liabru/matter-js/issues/862#issuecomment-635012278
I didn't even know that setting existed. 😄 The initial value is 0.05. I set it to 0. I didn't notice a difference.
Setting the Inertia
to Infinity
was another suggestion.
Here there's also energy being converted to torque, so try also setting the ball's
inertia
toinfinity
so the ball can not rotate, for me it then bounces for much longer - is this what you need? https://github.com/liabru/matter-js/issues/21#issuecomment-42775549
This didn't solve the problem, but it did change the results. The bouncing element will still lose momentum quickly, but not totally. When the distance between the two colliding elements is really close, the bouncing element no longer seems to lose momentum. That's not much new information, but that seemed like really strange behavior to me. Maybe that helps to track down this problem.
Here's some HTML to quickly test this problem... (It's a slightly modified version of the "Getting Started" Tutorial.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bounce Test</title>
<script src="matter.min.js"></script>
</head>
<body>
<script>
// module aliases
var Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies;
// create an engine
var engine = Engine.create();
// create a renderer
var render = Render.create({
element: document.body,
engine: engine
});
// create two boxes and a ground
var circle = Bodies.circle(400, 200, 80, { restitution: 1, friction: 0, frictionAir: 0, frictionStatic: 0 });
var square = Bodies.rectangle(200, 200, 40, 40, { restitution: 1, friction: 0, frictionAir: 0, frictionStatic: 0 });
var polygon = Bodies.polygon(600, 200, 1000, 80, { restitution: 1, friction: 0, frictionAir: 0, frictionStatic: 0 })
var ground = Bodies.rectangle(400, 610, 810, 60, { isStatic: true, restitution: 1, friction: 0, frictionAir: 0, frictionStatic: 0 });
// add all of the bodies to the world
World.add(engine.world, [circle, square, polygon, ground]);
// run the engine
Engine.run(engine);
// run the renderer
Render.run(render);
</script>
</body>
</html>
I saw the 0.15.0 update, and I thought that I saw this issue listed as closed, so I decided to test this issue once again. It seems that the problem still exists.
Through some experimentation, I realized that squares don't bounce indefinitely. They don't bounce very well at all actually. Also, liabru made an interesting suggestion...
Maybe try using Bodies.polygon with a high number of sides (like 50) to see if that helps.
That's why I looked more closely at the circle body.
It doesn't look like a circle. It looks like a 26-sided polygon. So, I added a 1000 sided polygon next to the circle. Surprisingly, the 1000-sided polygon bounces similarly. I thought it might be better at preserving energy. It is, but only towards the end. While the circle eventually stops, the polygon keeps bouncing — in a similar manner to the previous animated GIF.
Any updates on this issue?
I wanted to do a collision simulation, and a Brownian motion simulation but the energy loss in a fully elastic world is recognizable and the system becomes idle fairly quickly especially when a ball hits a stationary wall. In order to get the desired outcome, I had to implement the physics logic in the "collisionStart" event on the engine.
And here's what I did to achieve the Brownian motion...
Events.on(engine, "collisionStart", e => {
e.pairs.forEach(pair => {
const { bodyA, bodyB } = pair
if (bodyA.label === "wallH") Body.setVelocity(bodyB, { x: bodyB.velocity.x, y: -bodyB.velocity.y }) //Horizontal wall collision
else if (bodyB.label === "wallH") Body.setVelocity(bodyA, { x: bodyA.velocity.x, y: -bodyA.velocity.y }) //Horizontal wall collision
else if (bodyA.label === "wallV") Body.setVelocity(bodyB, { x: -bodyB.velocity.x, y: bodyB.velocity.y }) //Vertical wall collision
else if (bodyB.label === "wallV") Body.setVelocity(bodyA, { x: -bodyA.velocity.x, y: bodyA.velocity.y }) //Vertical wall collision
else { //Two balls collision
const vAXBefore = bodyA.velocity.x;
const vBXBefore = bodyB.velocity.x;
const vAYBefore = bodyA.velocity.y;
const vBYBefore = bodyB.velocity.y;
const mA = bodyA.mass
const mB = bodyB.mass
const { vAFinal: vAXFinal, vBFinal: vBXFinal } = calcElasticCollision(mA, mB, vAXBefore, vBXBefore)
const { vAFinal: vAYFinal, vBFinal: vBYFinal } = calcElasticCollision(mA, mB, vAYBefore, vBYBefore)
if (bodyA.label !== "wall") Body.setVelocity(bodyA, { x: vAXFinal, y: vBXFinal })
if (bodyB.label !== "wall") Body.setVelocity(bodyB, { x: vAYFinal, y: vBYFinal })
}
})
})
Add some walls
Composite.add(world, [
Bodies.rectangle(-50, canvasHeight / 2, 50, canvasHeight + 200, {
label: "wallV",
isStatic: true,
friction: 0,
frictionAir: 0,
frictionStatic: 0,
restitution: 1,
render: { visible: false }
}),
Bodies.rectangle(canvasWidth + 50, canvasHeight / 2, 50, canvasHeight + 200, {
label: "wallV",
isStatic: true,
friction: 0,
frictionAir: 0,
frictionStatic: 0,
restitution: 1,
render: { visible: false }
}),
Bodies.rectangle(canvasWidth / 2, -50, canvasWidth + 200, 50, {
label: "wallH",
isStatic: true,
friction: 0,
frictionAir: 0,
frictionStatic: 0,
restitution: 1,
render: { visible: false }
}),
Bodies.rectangle(canvasWidth / 2, canvasHeight + 50, canvasWidth + 200, 50, {
label: "wallH",
isStatic: true,
friction: 0,
frictionAir: 0,
frictionStatic: 0,
restitution: 1,
render: { visible: false }
}),
])
Then I add balls at random locations and set a random initial velocity to each one.
const balls = Array.from({ length: 100 }, () => Bodies.circle(getRandomNumber(5, canvasWidth - 5), getRandomNumber(5, canvasHeight - 5), 5, {
render: { fillStyle: "blue" },
frictionAir: 0,
friction: 0,
frictionStatic: 0,
inertia: Infinity,
restitution: 1,
}))
Composite.add(world, balls)
balls.forEach(ball => Body.setVelocity(ball, { x: getRandomNumber(-3, 3), y: getRandomNumber(-3, 3) }))
And here are the Helper functions:
const getRandomNumber = (min, max) => Math.random() * (max - min) + min
const calcElasticCollision = (mA, mB, vAInitial, vBInitial) => ({
vAFinal: (((mA - mB) / (mA + mB)) * vAInitial) + (((2 * mB) / (mA + mB)) * vBInitial),
vBFinal: (((2 * mA) / (mA + mB)) * vAInitial) + (((mB - mA) / (mA + mB)) * vBInitial)
})
The function calcElasticCollision
calculates the final velocity after an elastic collision according to the equations:
For more information search Elastic collisions or check the equations for a brief explanation on the wiki here
Doing this will provide you with a fully elastic collision system that never loses energy, this would work with one-dimensional collision too, but too complicated stuff I have no guarantees it'll work. Anyway, this is my workaround and I hope this helps someone who needed the same thing I did, and I hope this issue is solved and closed soon enough.
Regards,
@karimshalapy thanks for sharing!
This is still on my list of improvements I'd like to make (quite a long list though!)
For now my suggestions are the following as discussed earlier in the thread:
body.friction = 0
and body.frictionAir = 0
body.inertia = Infinity
body.slop
Engine.update(engine, 1)
and call multiple updates per frameengine.positionIterations = 20
and engine.velocityIterations = 20
Body.setVelocity(body, Vector.mult(Vector.normalise(body.velocity), speed))
Adding this line of code seems resolve the issue.
Matter.Resolver._restingThresh = 0.001;
@lout33 good point I think that could help! Only thing is it will apply globally to all bodies, but probably reasonable if you don't need resting collisions or if they still work fine with that setting. This is good info though for when I get around to visiting this topic properly, it may need some tuning or a new body property.
Adding this line of code seems resolve the issue.
Matter.Resolver._restingThresh = 0.001;
Thank you so much. I spent hours attempting every possible solution and this was the only solution that worked for me. What a nightmare.
In Phaser, you can use MatterWorldConfig.restingThresh property: https://newdocs.phaser.io/docs/3.55.2/Phaser.Types.Physics.Matter.MatterWorldConfig
Thanks for all the info and interaction in this thread! I ran into the same issue while using MatterJS with Phaser, and could not resolve it so far. Figured I'd add my reproducible scenario in case it's helpful in tweaking this.
For context, Phaser has a "bounce test" for its other (simpler) "Arcade" Physcis engine which at first seems to perfectly keep bouncing back to the same height, until you increase the gravity
from y: 150
to y: 151
: then it slowly starts loosing energy (though a lot slower than MatterJS, see below).
My repro is a fork from above test, where I've tried to apply really all of the fixes from this thread....
physics: {
default: 'matter',
matter: {
debug: true,
setBounds: true,
restingThresh: 0.00001,
positionIterations: 30,
velocityIterations: 25,
constraintIterations: 18,
runner: {
isFixed: true,
fps: 240,
correction: 10,
},
gravity: { y: 3 }
}
},
this.logo = this.matter.add.rectangle(300, 100, 40, 10, {
inertia: Infinity,
restitution: 1,
friction: 0,
frictionAir: 0,
frictionStatic: 0,
slop: 0.5,
});
A runnable example can be found at https://jsfiddle.net/jeroenheijmans/agncp79q/ and here's a screen recording of how quickly energy is lost from the system:
Most likely I incorrectly applied one of the abovementioned workarounds, if that's the case a suggested fix or better fork of the jsfiddle would be much appreciated.
Regardless, thank you for a wonderful piece of open source software! Much appreciated that we can all play around with this tool 💛
- prevent friction
body.friction = 0
andbody.frictionAir = 0
- prevent rotation
body.inertia = Infinity
- increase or decrease
body.slop
- use a small fixed timestep
Engine.update(engine, 1)
and call multiple updates per frame- use more iterations
engine.positionIterations = 20
andengine.velocityIterations = 20
- manually set a constant velocity on every frame
Body.setVelocity(body, Vector.mult(Vector.normalise(body.velocity), speed))
All of the above helped a little in my case, but this is what considerably reduced the energy loss:
- use a small fixed timestep
Engine.update(engine, 1)
and call multiple updates per frame
I've replaced:
Engine.update(engine, 1000 / fps)
with:
for (let i = 0; i < 1000 / fps; i++) {
Engine.update(engine, 1);
}
I'm using a modified version of the beachBalls example, with no friction, no air friction, and restitution of 1. When I let it run in the demo window, the balls eventually stop bouncing and come to a stop. I would expect them to keep bouncing indefinitely.
I also tried setting the restitution of the world boundaries to 1 (the code below is modified from the
Demo.reset
, and that did not help.I thought this might be an approximation issue when computing the effects of collisions and set high numbers for
positionIterations
,velocityIterations
, andconstraintIterations
.This did not help either.
Is this loss of energy due to numerical approximation errors, or can this energy loss be explicitly controlled? At the moment beyond adjusting restitution and friction I am unable to find ways to make energy conservation perfect.