2013-06-16 14 views
7

私はニュートンの方程式を使用して、私が現在取り組んでいるこのプログラムのボールを、互いに衝突するときに「分割」することができますが、ロットの問題を引き起こします。HTML5キャンバス - ボール物理学との衝突グリッチ

これは私のコードです:

<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> 

そして、あなたは多くの生成されたボールのを持っており、その速度が速いこれが起こる:

はなぜ?私はこれをどのように修正することができます誰も知っている?

+0

http://jsfiddle.net/bxy3p/ – super

+0

私はあなたの問題が何であるか把握するためにこれを読むつもりはないことを知っています。私は誰がなるのか分からない。 – duffymo

+0

私はあなたの問題が何であるかを既に知っています... –

答えて

2

あなたの解は、速度だけに基づいてペナルティ勢力を持つようです。これにより、球体が貫通することができ、いずれの球体にも速度がないときは、いずれも球体を固定しようとすることはありません。この問題を解決するには、位置ベースのペナルティを追加する必要があります。 非常に簡単な解決策は、スプリングを使用することです。貫通長さを計算し、Hooke's lawを使用して交差する球をお互いに押し込みます。

最善の解決策は、暗黙の接触解法を使用することです。それはもっと堅い接触を可能にするが、それのためのアルゴリズムはもっと複雑である。素早く良い結果を得るために、2D物理エンジンを使用することをお勧めします。JavaScript ports of Box2Dが最も使われているようです。

+0

はい、私はそのことについてBox2Dについて知っていますが、既存のライブラリを使用し、実際のコードが実際にどのように動作しているかわからず、問題を計算するための説明と実際の方程式が必要です。もちろん、 – super

+0

。ここにいくつかの良い読書のサイトです。それは私が取ったコースから来たものです。 http://www8.cs.umu.se/kurser/5DV058/HT12/5dv058sched.html – schteppe

+0

ナ、コース全体に行かなければなりませんか?あなたは説明していない、あなたは、ソースをしましたか? – super

0

私は上記の@ schteppeの答えに非常に敏感です。ここでこのリンクについて教えてください:http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/

「どうすればいいですか?」セクション。あなたの質問に答えてほしいと思うかもしれませんが、あなたのコードには間違いがあります。

私はまた、あなたのコードを機能させることを試みてきましたが、実質的な結果は得られませんでした。あなたがここに(私はあなたからコピー)私のバイオリンを確認することができます。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 = 'ABCDEF'.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(); 
    } 
} 

私のコードでは、ボールが固執することはできませんが、彼らは、攪拌の一定の状態です。 @schteppeによって提案されたようにフックの法則を実装しようとしていましたが、Box2dとその魔法について聞いたことがあります。

関連する問題