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
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". –
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
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