3

I am a beginner in canvas animation, I have tried a simple example code to do a circle collision test. Before that I have tried to search around internet but I can not understand what is the logic behind. Below is the code what i get so far, the problem is some of the circle they did the collision but after that they stick together or become overlay each other, not sure if anything I have missed or wrong in the logic behind?

 window.requestAnimationFrame= (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback){ window.setTimeout(callback, 1000 / 60); }; })(); (function(){ var c= document.getElementsByTagName('canvas')[0], can= c.getContext('2d'), ppl= [], count= 20; function resize(){ c.width= window.innerWidth, c.height= window.innerHeight, can.fillStyle='#000000', can.fillRect(0,0,c.width,c.height) } function pplD(){ var tf,pplnew={ x: Math.floor(Math.random()*c.width), y: Math.floor(Math.random()*c.height), size: 20,//Math.random()*4+8, vx: (Math.random()-0.5)*4+2, vy: (Math.random()-0.5)*4+2 } for(var i=0;i<ppl.length;i++){ tf= coli(pplnew,ppl[i]); if(tf){ pplD(); return } } return pplnew; } function canF(){ for(var i=0;i<count;i++){ ppl.push(new pplD) } requestAnimationFrame(render) } function coli(a,b){ var dis= a.size+b.size, disx= (a.x-b.x), disy= (a.y-b.y), disxy= Math.sqrt((disx*disx)+(disy*disy)), c; if(disxy<dis){ if((a.vx>0 && b.vx>0) || (a.vx<0 && b.vx<0)){ c= a.vx, a.vx= b.vx, b.vx= c }else{ a.vx*= -1, b.vx*= -1 } if((a.vy>0 && b.vy>0) || (a.vy<0 && b.vy<0)){ c= a.vy, a.vy= b.vy, b.vy= c }else{ a.vy*= -1, b.vy*= -1 } return true; } return false; } function drawppl(d,p){ var tf; for(var i=0;i<ppl.length;i++){ if(i==p) continue; tf= coli(d,ppl[i]); } if(d.x+d.size>c.width || d.x-d.size<0) d.vx=d.vx*-1; if(d.y+d.size>c.height || d.y-d.size<0) d.vy=d.vy*-1; d.x+= d.vx, d.y+= d.vy; can.fillStyle= '#a6e22e'; can.beginPath(); can.arc(d.x, d.y, d.size, 0, Math.PI*2, true); can.closePath(); can.fill() } function render(){ can.fillStyle='rgba(0,0,0,0.2)', can.fillRect(0,0,c.width,c.height) for(var i=0;i<ppl.length;i++){ drawppl(ppl[i],i) } requestAnimationFrame(render) } window.onresize= resize; resize(); canF() })();
html,body { width:100%; height:100%; margin:0; padding:0; border:0; }
<canvas></canvas>

3
  • code is ready to go by copy n paste in another html file. Commented Aug 12, 2016 at 8:49
  • ericleong.me/research/circle-circle Commented Aug 12, 2016 at 9:18
  • try reducing size of the balls or give it corresponding to the rectangle dimensions where the balls floating Commented Aug 12, 2016 at 9:31

1 Answer 1

1

You should to resolve future, not current colisions.
Math can be some difficult, but all your need - to take future positions in account.
Result velocity can't to be just negative of source, too.

I correct your function accordingly to this article

window.requestAnimationFrame= (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback){ window.setTimeout(callback, 1000 / 60); }; })(); (function(){ var c= document.getElementsByTagName('canvas')[0], can = c.getContext('2d'), ppl = [], count = 12; function resize(){ c.width = window.innerWidth; c.height = window.innerHeight; // console.log('Container', {x: c.width, y: c.height}); can.fillStyle='#000000', can.fillRect(0,0,c.width,c.height) } function pplD(){ var r = Math.random()*20+10; var tf,pplnew={ x: Math.floor(Math.random()*c.width-2*r)+3*r, y: Math.floor(Math.random()*c.height-2*r)+3*r, r: r, m: r/30, // mass vx: (Math.random()-0.5)*4+2, vy: (Math.random()-0.5)*4+2 } for(var i=0;i<ppl.length;i++){ resA(pplnew,ppl[i]); } return pplnew; } function canF(){ for(var i=0;i<count;i++){ ppl.push(new pplD) } // make heavy one ppl[0].m = 5; requestAnimationFrame(render) } function distance(a, b) { return Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2)); } function midpoint(a, b) { return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }; } function resA(a, b) { var mid = midpoint(a, b); var dist = distance(a, b); if (dist > a.r+b.r) return; a.x = mid.x + (a.r+b.r) * (a.x - b.x) / dist; a.y = mid.y + (a.r+b.r) * (a.y - b.y) / dist; } function staticStaticResolve(a, b) { var mid = midpoint(a, b); var dist = distance(a, b); if (dist > a.r+b.r) return; a.x = mid.x + a.r * (a.x - b.x) / dist; a.y = mid.y + a.r * (a.y - b.y) / dist; b.x = mid.x + b.r * (b.x - a.x) / dist; b.y = mid.y + b.r * (b.y - a.y) / dist; } function coli(a, b) { var a1 = { x: a.x + a.vx, y: a.y + a.vy }; var b1 = { x: b.x + b.vx, y: b.y + b.vy }; var d = distance(a1, b1); if (d > a.r+b.r) return; var n = { x: (b1.x - a1.x) / d, y: (b1.y - a1.y) / d }; var p = 2 * (a.vx*n.x + a.vy*n.y - (b.vx*n.x + b.vy*n.y)) / (a.m + b.m); a.vx = a.vx - p * b.m * n.x; a.vy = a.vy - p * b.m * n.y; b.vx = b.vx + p * a.m * n.x; b.vy = b.vy + p * a.m * n.y; } function drawppl(d, p){ for(var i=0;i<ppl.length;i++){ if(i==p) continue; coli(d,ppl[i]); } if(d.x+d.r>c.width) d.vx=-Math.abs(d.vx); if(d.x-d.r<0) d.vx=Math.abs(d.vx); if(d.y+d.r>c.height) d.vy=-Math.abs(d.vy); if(d.y-d.r<0) d.vy=Math.abs(d.vy); d.x += d.vx, d.y += d.vy; can.beginPath(); can.arc(d.x, d.y, d.r, 0, Math.PI*2, true); can.closePath(); //can.fillStyle= d.m > 3 ? '#ff6666' : '#66ff66'; //can.fill(); can.lineWidth = 3; can.strokeStyle= d.m > 3 ? '#ff6666' : '#66ff66'; can.stroke(); } function render(){ can.fillStyle='rgba(0,0,0,0.2)', can.fillRect(0,0,c.width,c.height) for(var i=0;i<ppl.length;i++){ drawppl(ppl[i],i) } requestAnimationFrame(render) } window.onresize= resize; resize(); canF(); })();
html,body { width:100%; height:100%; margin:0; padding:0; border:0; }
<canvas></canvas>

Let's try to resolve "eating" issue.
We just will resolve them as static, when they are too near.

window.requestAnimationFrame= (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback){ window.setTimeout(callback, 1000 / 60); }; })(); (function(){ var c= document.getElementsByTagName('canvas')[0], can = c.getContext('2d'), ppl = [], count = 12; function resize(){ c.width = window.innerWidth; c.height = window.innerHeight; // console.log('Container', {x: c.width, y: c.height}); can.fillStyle='#000000'; can.fillRect(0,0,c.width,c.height) } function pplD(){ var r = Math.random()*20+10; var tf,pplnew={ x: Math.floor(Math.random()*c.width-2*r)+3*r, y: Math.floor(Math.random()*c.height-2*r)+3*r, r: r, m: r/30, // mass vx: (Math.random()-0.5)*4+2, vy: (Math.random()-0.5)*4+2 } for(var i=0;i<ppl.length;i++){ resA(pplnew,ppl[i]); } return pplnew; } function canF(){ for(var i=0;i<count;i++){ ppl.push(new pplD) } // make heavy one ppl[0].m = 5; requestAnimationFrame(render) } function distance(a, b) { return Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2)); } function midpoint(a, b) { return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }; } function resA(a, b) { var mid = midpoint(a, b); var dist = distance(a, b); if (dist > a.r+b.r) return; a.x = mid.x + (a.r+b.r) * (a.x - b.x) / dist; a.y = mid.y + (a.r+b.r) * (a.y - b.y) / dist; } function staticStaticResolve(a, b) { var mid = midpoint(a, b); var dist = distance(a, b); if (dist > a.r+b.r) return; a.x = mid.x + a.r * (a.x - b.x) / dist; a.y = mid.y + a.r * (a.y - b.y) / dist; b.x = mid.x + b.r * (b.x - a.x) / dist; b.y = mid.y + b.r * (b.y - a.y) / dist; } function coli(a, b) { var a1 = { x: a.x + a.vx, y: a.y + a.vy }; var b1 = { x: b.x + b.vx, y: b.y + b.vy }; var d = distance(a1, b1); if (d > a.r+b.r) return; if (d < Math.min(a.r, b.r)) { staticStaticResolve(a, b); } var n = { x: (b1.x - a1.x) / d, y: (b1.y - a1.y) / d }; var p = 2 * (a.vx*n.x + a.vy*n.y - (b.vx*n.x + b.vy*n.y)) / (a.m + b.m); a.vx = a.vx - p * b.m * n.x; a.vy = a.vy - p * b.m * n.y; b.vx = b.vx + p * a.m * n.x; b.vy = b.vy + p * a.m * n.y; } function drawppl(d, p){ for(var i=0;i<ppl.length;i++){ if(i==p) continue; coli(d,ppl[i]); } if(d.x+d.r>c.width) d.vx=-Math.abs(d.vx); if(d.x-d.r<0) d.vx=Math.abs(d.vx); if(d.y+d.r>c.height) d.vy=-Math.abs(d.vy); if(d.y-d.r<0) d.vy=Math.abs(d.vy); d.x += d.vx, d.y += d.vy; can.beginPath(); can.arc(d.x, d.y, d.r, 0, Math.PI*2, true); can.closePath(); //can.fillStyle= d.m > 3 ? '#ff6666' : '#66ff66'; //can.fill(); can.lineWidth = 2; can.strokeStyle= d.m > 3 ? '#ff6666' : '#66ff66'; can.stroke(); } var times = []; function render(){ times = times.filter(t => t>Date.now()-1000); times.push(Date.now()); can.fillStyle='rgba(0,0,0,0.5)', can.fillRect(0,0,c.width,c.height) can.font = "30px Arial"; can.fillStyle='#ffffff'; can.fillText("FPS: "+ times.length,10,50); for(var i=0;i<ppl.length;i++){ drawppl(ppl[i],i) } requestAnimationFrame(render) } window.onresize= resize; resize(); canF(); })();
html,body { width:100%; height:100%; margin:0; padding:0; border:0; }
<canvas></canvas>

Sign up to request clarification or add additional context in comments.

1 Comment

There is a bug with "eating" still. I think it's because we count not then collision happens inside of circle. Also we count not multiple collisions at the same time.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.