画布圆检测碰撞和响应

时间:2016-08-12 08:47:31

标签: javascript canvas collision

我是画布动画的初学者,我尝试过一个简单的示例代码来进行圆碰撞测试。在此之前,我试图搜索互联网,但我无法理解背后的逻辑是什么。下面是我到目前为止得到的代码,问题是他们碰撞的一些圈子但是之后他们粘在一起或者互相叠加,不确定我背后的逻辑中是否有任何遗漏或错误?

    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>

1 个答案:

答案 0 :(得分:2)

你应该解决未来的问题,而不是当前的问题 数学可能有些困难,但你需要的全部 - 考虑未来的职位 结果速度也不能只是消极来源。

我相应地更正了this article

的功能

&#13;
&#13;
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();
})();
&#13;
html,body {
  width:100%;
  height:100%;
  margin:0;
  padding:0;
  border:0;
}
&#13;
<canvas></canvas>
&#13;
&#13;
&#13;

让我们尝试解决&#34;吃&#34;问题。
当它们太近时,我们只会将它们解析为静态。

&#13;
&#13;
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();
})();
&#13;
html,body {
  width:100%;
  height:100%;
  margin:0;
  padding:0;
  border:0;
}
&#13;
<canvas></canvas>
&#13;
&#13;
&#13;