2013-07-14 17 views
6

Ciao a tutti
Ho iniziato a scrivere un piccolo gioco con sfere e mattoni e ho qualche problema con il rilevamento delle collisioni. Ecco il mio codice http://jsbin.com/ibufux/9. So che il rilevamento funziona anche se array, ma non riesco a capire come posso applicarlo al mio codice.
Rilevamento della collisione attraverso l'array

Ecco quello che ho provato:

bricksCollision: function() { 
     for (var i = 0; i < $bricks.length; i++) { 
       if ($ball.t == $bricks[i].offset().top) { 
        $bricks[i].splice(i, 1); 
       } 
     } 

Ogni mattoni in gioco sono generati dal ciclo for e poi va a $ mattoni array. Ogni mattone dopo aver generato riceve la posizione superiore e sinistra e ha la posizione assoluta. Ho provato a controllare se $ ball.t (è proprietà dell'oggetto della mia palla che rileva la posizione superiore della palla) raggiunge i mattoni e rimuove i mattoni.

Grazie per qualsiasi aiuto. Sto solo iniziando a imparare JS, ecco perché il mio codice è knotty.

+7

Solo una nota, non impone JavaScript una convenzione '$ varName' come alcune [altre lingue] (http://www.php.net). Puoi chiamare i mattoni della tua serie di mattoni invece di "$ mattoni". –

+0

Se ho capito bene, stai confrontando solo i valori più alti e i valori di sinistra. Ciò significa che i due oggetti devono avere esattamente gli stessi valori. Suppongo che dovresti prendere almeno la larghezza in considerazione durante il confronto, probabilmente anche in altezza. – bestprogrammerintheworld

+0

Il problema è che non riesco a rilevare la posizione più alta dei miei elementi nell'array $ brick. Ho provato la funzione jQuery ** $ brick [i] .offset(). Top ** ma la console mostra ** Uncaught TypeError: l'oggetto 0 non ha alcun metodo 'offset' ** – kuzyoy

risposta

5

Prima di tutto, let'stalk su alcuni errori di codice

  • $ball.t dovrebbe essere probabilmente $ball.top
  • non c'è bisogno di avere $ come prefisso, per il codice, è semplicemente una variabile e si chiamano $ball invece di ball risultati streghe in errori di assunzione!

per quegli errori assunto è quello che si sta facendo male:

  • $ball è un elemento DOM, non un elemento jQuery
  • lo stesso $bricks
  • $ball è un array

con quelli conclusi da alcuni console.log() proviamo a risolvere il codice:

il $ball dovrebbe essere chiamato, in quanto c'è una sola, da esso è elemento di un array, come $ball[0] e perché avete variabili indicando elementi DOM e non elementi jQuery, è necessario avvolgerlo in Jquery come:

if ($($ball[0]).top === $($bricks[i]).offset().top) { ... 

una buona idea per non confondersi è usare solo $ in elementi jQuery, il prefisso in una variabile, non li rende un elemento jQuery.

E ogni volta che si nota un errore come "l'elemento x non ha metodo y" presuppone sempre che si stia chiamando un metodo da un elemento DOM e non da un elemento jQuery.

+0

Grazie @balexandre . Sembra di aver capito e $ ($ brick [i]). Offset(). Top) funziona perfettamente – kuzyoy

2

Ora che @balexandre ha ben spiegato alcuni punti sul codice, esaminiamo come possiamo calcolare la collisione.

Imagine 2 intervalli sovrapposti reciprocamente (Intervallo un sovrappone parzialmente Gamma b)

[100  .|..  300] 
      [200  ..|.  400] 

La sovrapposizione parte è da | a | -> 200 a 300, quindi la dimensione della sovrapposizione è 100

Se si guardano i numeri, si nota, che la sovrapposizione potrebbe essere visto come,

  • prendere il numero più piccolo del lato destro -> 300
  • prendere il numero greate del lato sinistro -> 200
  • sottrarre loro le une dalle altre -> 300 - 200 = 100.

consente di dare un'occhiata in altri 2 casi. (Gamma b completamente nella gamma un)

[50 ... 150] 
    [75...125] 

Così i valori che abbiamo sono: Math.min (150,125) //125 per il valore finale e Math.max (50,75) // 75 per il valore iniziale, con un conseguente valore 125-75 = 50 per il sovrappongono

Diamo uno sguardo l'ultimo esempio (Gamma un non nel campo b)

[50 ... 150] 
         [200 ... 300] 

Usando la formula di cui sopra, i rendimenti il ​​risultato Math.min (150 , 300) - Math.max (50,200) // -50 che absolutes' valore è il divario tra i 2 campi, 50

Ora possiamo aggiungere un ultima condizione, come si vuole calcolare la collisione, solo valori > 0 sono di interesse per noi. Detto questo possiamo metterlo in una condizione.

Math.min ((Brick["Right"],Ball["Right"]) - Math.max (Brick["Left"], Ball["Left"]) > 0)

che produrrà true se si sovrappongono gli elementi e false se non lo fanno.

Applicando questo al codice, potremmo calcolare la collisione seguente modo

bricksCollision: function() { 

    for (var i = 0; i < $bricks.length; i++) { 
     var $brick = $($bricks[i]); 
     var offset = $brick.offset(); 
     var brickBounds = [offset.left - field.l]; //brick left pos 
     brickBounds[1] = brickBounds[0] + 40 //bricks right pos -> left pos + .bricks.width; 
     var ballBounds = [ball.l]; //balls left pos 
     ballBounds[1] = ballBounds[0] + 20 //balls right pos -> left pos + #ball.width; 
     if (ball.t <= (offset.top + 20) && (Math.min(brickBounds[1], ballBounds[1]) - Math.max(brickBounds[0], ballBounds[0])) > 0) { 

      $bricks[i].style.opacity = 0; //Make the brick opaque so it is not visible anymore 
      $bricks.splice(i, 1) //remove the brick from the array -> splice on the array, not the element 

      return true; 
     } 
    } 
} 

Con questo potremmo restituire true alla funzione movimento, quando la palla si scontra con un mattone.

Ma hey, vogliamo che rimbalzi nella giusta direzione, quindi affronteremo un altro problema. Quindi, piuttosto che restituire un valore booleano, indipendentemente dal fatto che il mattone entri in conflitto o meno, potremmo restituire una nuova direzione in cui la palla dovrà spostarsi.

Per poter modificare facilmente solo la x o la parte y della direzione, dovremmo usare qualcosa come un vettore.

A questo scopo, abbiamo potuto utilizzare 2 bit di un intero, dove i bit b0 soggiorni per la direzione x ed il bit b1 per la direzione y. Tale.

Dec  Bin  Direction   

0 -> 00 -> Down Left 
      ^ -> Left 
      ^ -> Down 
1 -> 01 -> Down Right 
      ^ -> Right 
      ^ -> Down 
2 -> 10 -> Up Left 
      ^ -> Left 
      ^ -> Up 
3 -> 11 -> Up Right 
      ^ -> Right 
      ^ -> Up 

Ma per poter modificare solo una parte della direzione, dobbiamo passare la vecchia direzione alla funzione collisione, e utilizzare bitwise & e | rispettivamente disattivarli o

anche dobbiamo calcolare da che parte la palla si scontra. Fortunatamente abbiamo il calcolo della sovrapposizione di prima, che utilizza già tutti i valori di cui abbiamo bisogno, per calcolare la direzione della collisione.

Se si tratta frome il

  • destra
    • Brick [ "Destra"] - Ball [ "sinistra"] deve essere lo stesso valore di sovrapposizione.
  • sinistra
    • Ball [ "Destra"] - Brick [ "sinistra"] deve essere lo stesso valore di sovrapposizione.

Se nessuno di loro sono vere, si deve o venire dal

  • fondo
    • se Ball [ "Top"] è più che (Brick [ "Top" ] più metà del mattone [ "altezza"])

oppure dall'alto.

Per ridurre l'intervallo in cui la condizione, per la collisione di lato, restituisce true possiamo aggiungere un'altra condizione che la sovrapposizione deve essere inferiore ad esempio ... && overlap < 2

Quindi, se si scontra con il bordo doesn Rimbalzo sempre di lato.


Così basta parlare, nel codice questo potrebbe sembrare qualcosa di simile.

bricksCollision: function (direction) { 
    var newDirection = direction 

    var ballBounds = [ball.l]; //balls left pos 
    ballBounds[1] = ballBounds[0] + 20 //balls right pos -> left pos + #ball.width; 


    for (var i = 0; i < $bricks.length; i++) { 
     var $brick = $($bricks[i]); 
     var offset = $brick.offset(); 

     var brickBounds = [offset.left - field.l]; //brick left pos 
     brickBounds[1] = brickBounds[0] + 40 //bricks right pos -> left pos + .bricks.width; 


     var overlap = Math.min(brickBounds[1], ballBounds[1]) - Math.max(brickBounds[0], ballBounds[0]); 

     if (ball.t <= ((offset.top - field.t) + 20) && overlap > 0) { 

      $bricks[i].style.opacity = 0; //Make the brick opaque so it is not visible anymore 
      $bricks.splice(i, 1) //remove the brick from the array -> splice on the array, not the element 


      if (ballBounds[1] - brickBounds[0] == overlap && overlap < 2) { //ball comes from the left side 
       newDirection &= ~(1); //Turn the right bit off -> set x direction to left 
      } else if (brickBounds[1] - ballBounds[0] == overlap && overlap < 2) { //ball comes from the right side 
       newDirection |= 1; // Turn the right bit on -> set x direction to right; 
      } else { 
       if (ball.t > (offset.top + (20/2))) //Ball comes from downwards 
        newDirection &= ~(2) // Turn the left bit off -> set y direction to down; 
       else //Ball comes from upwards 
        newDirection |= 2; // Turn the left bit on -> set y direction to up; 
      } 
      //console.log("Coming from: %s Going to: %s", field.directionsLkp[direction], field.directionsLkp[newDirection], direction) 
      return newDirection; 
     } 
    } 
    return direction; 
} 

per ottenere che al lavoro, dobbiamo anche cambiare le moveXX funzioni, di utilizzare la nuova direzione, restituito.

Ma se stiamo per ottenere la nuova direzione dalla funzione collisione comunque, potremmo spostare la rilevazione completa della collisione sulla funzione, per semplificare le nostre funzioni di spostamento. Ma prima dovremmo dare un'occhiata alle funzioni di spostamento e, aggiungere un oggetto di ricerca a field che contiene i numeri per la direzione, per mantenere la leggibilità.

var field = { 
    directions: { 
    uR : 3, // 11 
    dR : 1, // 01 
    dL : 0, // 00 
    uL : 2 // 10 

    }, 
    directionsLkp: [ 
    "dL","dR","uL","uR" 
    ], 
... 
} 

Ora le funzioni mossa potrebbe quindi simile a questa,

ballCondact: function() { 
     var moves = [moveDl,moveDr,moveUl,moveUr] 
     var timeout = 5; 

     function moveUr() { 
      var timer = setInterval(function() { 
       $ball.css({ 
        top: (ball.t--) + "px", 
        left: (ball.l++) + "px" 
       }) 
       var newDirection = game.bricksCollision(field.directions.uR) //get the new direction from the collision function 
       if (newDirection !== field.directions.uR) { 
        clearInterval(timer); 
        moves[newDirection](); //move in the new direction 
       } 
      }, timeout); 
     } 
... 
} 

In questo modo, la funzione di spostamento cambia semplicemente la direzione se la funzione di collisione restituisce una direzione diversa da quella attuale.

Ora possiamo iniziare a spostare le collisioni sulla parete nella funzione di collisione, per fare ciò potremmo aggiungere un altro controllo all'inizio.

bricksCollision: function (direction) { 

     ... 

     if (ball.t <= field.t) 
     newDirection &= ~(2); //Ball is at top, move down 
     else if (ball.l <= 0) //Ball is at the left, move right 
     newDirection |= 1; 
     else if (ball.t >= field.b - ball.height) //Ball is at the bottom, move up 
     newDirection |= 2; 
     else if (ball.l > field.width - ball.width) //Ball is at the right, move left 
     newDirection &= ~(1); 

     if (direction !== newDirection) 
     return newDirection 

     ... 
} 

nota, ho lasciato fuori il controllo di collisione per la piattaforma, come l'idea dovrebbe essere chiaro =)

Ecco un Fiddle

Problemi correlati