-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Unexpected loss of energy in frictionless perfectly elastic setting #256
Comments
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.
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) 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 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 { Any suggestion? |
in beforeUpdate event:
|
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. With the Workaround of @Menowa1709 the energy is conserved, walls are not sticky and the angle looks fine. The problem of the workaround is ignoring inertia and friction if it is switched on. |
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.
I didn't even know that setting existed. 😄 Setting the
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...
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...
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. 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 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. 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:
|
Adding this line of code seems resolve the issue.
|
@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. |
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: |
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 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 💛 |
All of the above helped a little in my case, but this is what considerably reduced the energy loss:
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.
The text was updated successfully, but these errors were encountered: