HTML5 Canvas - Crashing into Ball Physics

Im using the Newtonian equations to make the balls in this program that I am working on now, β€œcrack” when they collide with each other, but sometimes they get stuck in each other, and this causes a lot of problems.

.

This is my code:

<center> <canvas id="canvas" style="border: 2px solid black; cursor: crosshair;" width="1000" height="500"></canvas> </center> <script> var canvas = document.getElementById("canvas") var ctx = canvas.getContext("2d") var w = canvas.width var h = canvas.height var ball = [] var gravity = 0.3 var force = 0.2 var mouse = { d: false, x1: 0, y1: 0, x2: 0, y2: 0, } window.onmousedown = function(e) { mouse.d = true mouse.x1 = mouse.x2 = e.pageX - canvas.getBoundingClientRect().left mouse.y1 = mouse.y2 = e.pageY - canvas.getBoundingClientRect().top } window.onmousemove = function(e) { if (mouse.d) { mouse.x2 = e.pageX - canvas.getBoundingClientRect().left mouse.y2 = e.pageY - canvas.getBoundingClientRect().top } else { mouse.x1 = mouse.x2 = e.pageX - canvas.getBoundingClientRect().left mouse.y1 = mouse.y2 = e.pageY - canvas.getBoundingClientRect().top } } window.onmouseup = function() { if (mouse.d) { mouse.d = false var dx = (mouse.x1 - mouse.x2); var dy = (mouse.y1 - mouse.y2); var mag = Math.sqrt(dx * dx + dy * dy); ball.push({ x: mouse.x1, y: mouse.y1, r: Math.floor(Math.random() * 20) + 10, vx: dx / mag * -(mag * force), vy: dy / mag * -(mag * force), b: 0.7, }) } } document.onselectstart = function() {return false} document.oncontextmenu = function() {return false} setInterval(update, 1000 / 60) function update() { ctx.clearRect(0, 0, w, h) ctx.beginPath() ctx.moveTo(mouse.x1, mouse.y1) ctx.lineTo(mouse.x2, mouse.y2) ctx.stroke() ctx.closePath() for (i = 0; i < ball.length; i++) { ball[i].vy += gravity ball[i].x += ball[i].vx ball[i].y += ball[i].vy if (ball[i].x > w - ball[i].r) { ball[i].x = w - ball[i].r ball[i].vx *= -ball[i].b } if (ball[i].x < ball[i].r) { ball[i].x = ball[i].r ball[i].vx *= -ball[i].b } if (ball[i].y > h - ball[i].r) { ball[i].y = h - ball[i].r ball[i].vy *= -ball[i].b } if (ball[i].y < ball[i].r) { ball[i].y = ball[i].r ball[i].vy *= -ball[i].b } for (j = i + 1; j < ball.length; j++) { var dx = ball[i].x - ball[j].x var dy = ball[i].y - ball[j].y var dist = Math.sqrt(dx * dx + dy * dy) if (Math.abs(dx) + Math.abs(dy) != 0 && dist <= ball[i].r + ball[j].r) { var angle = Math.atan2(dy, dx) var sp1 = Math.sqrt(ball[i].vx*ball[i].vx + ball[i].vy*ball[i].vy); var sp2 = Math.sqrt(ball[j].vx*ball[j].vx + ball[j].vy*ball[j].vy); var dir1 = Math.atan2(ball[i].vy, ball[i].vx); var dir2 = Math.atan2(ball[j].vy, ball[j].vx); var vx1 = sp1 * Math.cos(dir1 - angle); var vy1 = sp1 * Math.sin(dir1 - angle); var vx2 = sp2 * Math.cos(dir2 - angle); var vy2 = sp2 * Math.sin(dir2 - angle); var fvx1 = ((ball[i].r - ball[j].r) * vx1 + (2 * ball[j].r) * vx2) / (ball[i].r + ball[j].r); var fvx2 = ((2 * ball[i].r) * vx1 + (ball[j].r - ball[i].r) * vx2) / (ball[i].r + ball[j].r); var fvy1 = vy1; var fvy2 = vy2; ball[i].vx = Math.cos(angle) * fvx1 + Math.cos(angle + Math.PI/2) * fvy1; ball[i].vy = Math.sin(angle) * fvx1 + Math.sin(angle + Math.PI/2) * fvy1; ball[j].vx = Math.cos(angle) * fvx2 + Math.cos(angle + Math.PI/2) * fvy2; ball[j].vy = Math.sin(angle) * fvx2 + Math.sin(angle + Math.PI/2) * fvy2; } } ctx.beginPath() ctx.arc(ball[i].x, ball[i].y, ball[i].r, 0, Math.PI * 2, false) ctx.fillStyle = "black" ctx.fill() ctx.closePath() } } </script> 

And when you have a lot of balls, and their speed is fast, this happens:

Why? Does anyone know how I can fix this?

+7
source share
2 answers

Your decision seems to have the power of punishment based only on speed. This allows the spheres to penetrate, and when there is no speed in any of the spheres, none of them will try to fix the penetration. To solve this problem, you need to add a penalty based on the position. A very simple solution is to use springs. Calculate the penetration length and click the intersecting spheres from each other using Hooke's Law .

A better solution would be to use an implicit contact resolution algorithm. This allows you to establish more rigid contacts, but the algorithms for this are more complex. I suggest you use a 2D physics engine for fast and good results: Box2D's JavaScript ports are probably the most used.

+2
source

I really like @schteppe's answer above. I just want to tell you about this link here: http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/

Check "Why should I use it?" section. It may seem that there are some hints to the answer to your question about what exactly is wrong with your code.

I also tried to get your code to work, by the way, but without significant result. You can check my fiddle (which I copied from you) here: http://jsfiddle.net/sukhmeetsd/joqpqp49/

 var canvas = document.getElementById("canvas") var ctx = canvas.getContext("2d") var w = canvas.width var h = canvas.height var d = 5; //distance to move on collision var ball = [] var gravity = 0.3 var force = 0.2 var mouse = { d: false, x1: 0, y1: 0, x2: 0, y2: 0, } window.onmousedown = function (e) { mouse.d = true mouse.x1 = mouse.x2 = e.pageX - canvas.getBoundingClientRect().left mouse.y1 = mouse.y2 = e.pageY - canvas.getBoundingClientRect().top } window.onmousemove = function (e) { if (mouse.d) { mouse.x2 = e.pageX - canvas.getBoundingClientRect().left mouse.y2 = e.pageY - canvas.getBoundingClientRect().top } else { mouse.x1 = mouse.x2 = e.pageX - canvas.getBoundingClientRect().left mouse.y1 = mouse.y2 = e.pageY - canvas.getBoundingClientRect().top } } window.onmouseup = function () { if (mouse.d) { mouse.d = false var dx = (mouse.x1 - mouse.x2); var dy = (mouse.y1 - mouse.y2); var mag = Math.sqrt(dx * dx + dy * dy); ball.push({ x: mouse.x1, y: mouse.y1, r: Math.floor(Math.random() * 20) + 10, vx: dx / mag * -(mag * force), vy: dy / mag * -(mag * force), b: 0.7, }) } } function getRandomColor() { var letters = '0123456789ABCDEF'.split(''); var color = '#'; for (var i = 0; i < 6; i++ ) { color += letters[Math.floor(Math.random() * 16)]; } return color; } document.onselectstart = function () { return false } document.oncontextmenu = function () { return false } setInterval(update, 1000/60) function update() { ctx.clearRect(0, 0, w, h) ctx.beginPath() ctx.moveTo(mouse.x1, mouse.y1) ctx.lineTo(mouse.x2, mouse.y2) ctx.stroke() ctx.closePath() for (i = 0; i < ball.length; i++) { ball[i].vy += gravity ball[i].x += ball[i].vx ball[i].y += ball[i].vy if (ball[i].x > w - ball[i].r) { ball[i].x = w - ball[i].r ball[i].vx *= -ball[i].b } if (ball[i].x < ball[i].r) { ball[i].x = ball[i].r ball[i].vx *= -ball[i].b } if (ball[i].y > h - ball[i].r) { ball[i].y = h - ball[i].r ball[i].vy *= -ball[i].b } if (ball[i].y < ball[i].r) { ball[i].y = ball[i].r ball[i].vy *= -ball[i].b } for (j = i + 1; j < ball.length; j++) { var dx = ball[i].x - ball[j].x var dy = ball[i].y - ball[j].y var dist = Math.sqrt(dx * dx + dy * dy) if (Math.abs(dx) + Math.abs(dy) != 0 && dist <= ball[i].r + ball[j].r) { var angle = Math.atan2(dy, dx) var sp1 = Math.sqrt(ball[i].vx * ball[i].vx + ball[i].vy * ball[i].vy); var sp2 = Math.sqrt(ball[j].vx * ball[j].vx + ball[j].vy * ball[j].vy); var dir1 = Math.atan2(ball[i].vy, ball[i].vx); var dir2 = Math.atan2(ball[j].vy, ball[j].vx); d = Math.ceil(ball[i].r+ball[j].r-dist)/2; //moving them back ball[i].x = ball[i].x - Math.cos(dir1)*d-1; ball[i].y = ball[i].y - Math.sin(dir1)*d-1; ball[j].x = ball[j].x + Math.cos(dir2)*d+1; ball[j].y = ball[j].y + Math.sin(dir2)*d+1; //Checking for distance again /*dx = ball[i].x - ball[j].x; dy = ball[i].y - ball[j].y; dist = Math.sqrt(dx * dx + dy * dy); if (Math.abs(dx) + Math.abs(dy) != 0 && dist <= ball[i].r + ball[j].r){ ball[i].x = ball[i].x + Math.cos(dir1)*2*d; ball[i].y = ball[i].y + Math.sin(dir1)*d*2; ball[j].x = ball[j].x - Math.cos(dir2)*d*2; ball[j].y = ball[j].y - Math.sin(dir2)*d*2; }*/ var vx1 = sp1 * Math.cos(dir1 - angle); var vy1 = sp1 * Math.sin(dir1 - angle); var vx2 = sp2 * Math.cos(dir2 - angle); var vy2 = sp2 * Math.sin(dir2 - angle); var fvx1 = ((ball[i].r - ball[j].r) * vx1 + (2 * ball[j].r) * vx2) / (ball[i].r + ball[j].r); var fvx2 = ((2 * ball[i].r) * vx1 + (ball[j].r - ball[i].r) * vx2) / (ball[i].r + ball[j].r); var fvy1 = vy1; var fvy2 = vy2; ball[i].vx = Math.cos(angle) * fvx1 + Math.cos(angle + Math.PI / 2) * fvy1; ball[i].vy = Math.sin(angle) * fvx1 + Math.sin(angle + Math.PI / 2) * fvy1; ball[j].vx = Math.cos(angle) * fvx2 + Math.cos(angle + Math.PI / 2) * fvy2; ball[j].vy = Math.sin(angle) * fvx2 + Math.sin(angle + Math.PI / 2) * fvy2; } } ctx.beginPath() ctx.arc(ball[i].x, ball[i].y, ball[i].r, 0, Math.PI * 2, false) ctx.fillStyle = getRandomColor(); ctx.fill(); ctx.closePath(); } } 

My code doesn’t let the balls go, but they are in a constant state of excitement. I was about to implement Hooke's law proposed by @schteppe, but then I heard about Box2d and its magic.

0
source

All Articles