2013-06-16 18 views
7

Im usando le equazioni di Newton per fare in modo che le palle in questo programma su cui sto lavorando siano "divise" quando si scontrano tra loro, ma a volte si incastrano e causa molti problemi.HTML5 Canvas - Collisione con glitch fisica delle sfere

.

Questo è il mio codice:

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

e quando si ha una sacco di palle generato e la loro velocità è veloce questo accade:

Perché? Qualcuno sa come posso risolvere questo?

+0

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

+0

So che non sono disposto a leggere questo per capire qual è il tuo problema. Non so chi sarà. – duffymo

+0

So già qual è il tuo problema ... –

risposta

2

La tua soluzione sembra avere una forza di penalità basata solo sulla velocità. Questo permette alle sfere di penetrare, e quando non c'è velocità in nessuna delle sfere, nessuno di loro proverà a fissare la penetrazione. Per risolvere questo problema, è necessario aggiungere una penalità basata sulla posizione. Una soluzione molto semplice è usare le molle. Calcola la lunghezza della penetrazione e spinge le sfere intersecanti l'una dall'altra usando Hooke's law.

La soluzione migliore sarebbe utilizzare un algoritmo implicito di risoluzione dei contatti. Ciò consente contatti più rigidi, ma gli algoritmi per questo sono molto più complicati. Ti suggerisco di utilizzare un motore fisico 2D per risultati rapidi e buoni: lo JavaScript ports of Box2D sembra essere il più usato.

+0

Sì, lo so, Box2D, ma voglio ancora una spiegazione e un'equazione reale per calcolare il problema invece di usare una libreria esistente e non sapere come funziona davvero il mio codice. – super

+0

Ovviamente. Ecco un sito con una buona lettura. È da un corso che ho seguito. http://www8.cs.umu.se/kurser/5DV058/HT12/5dv058sched.html – schteppe

+0

Nah, devo seguire un corso completo? Non puoi spiegare, hai fatto il coraggio, giusto? – super

0

Sono molto soddisfatto della risposta di @ schteppe sopra. Volevo solo parlarvi di questo link: http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/

Controllare "Perché dovrei usarlo?" sezione. Potrebbe rivelarsi un accenno alla risposta alla tua domanda che ciò che è esattamente sbagliato nel tuo codice.

Ho anche cercato di far funzionare il codice, tra l'altro, ma senza risultati sostanziali. È possibile controllare il mio violino (che ho copiato dal tuo) qui: 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(); 
    } 
} 

Il mio codice non lasciate che le palline si attaccano ma sono in un costante stato di agitazione. Stavo per implementare la legge di Hooke come suggerito da @schteppe, ma poi ho sentito parlare di Box2d e della sua magia.