2013-07-14 14 views
6

Hallo allerseits
Ich fing an, ein kleines Spiel mit Ball und Ziegeln zu schreiben und habe ein Problem mit der Kollisionserkennung. Hier ist mein Code http://jsbin.com/ibufux/9. Ich weiß, dass Erkennung durch Array funktioniert, aber ich kann nicht herausfinden, wie ich es auf meinen Code anwenden kann. Hier
Kollisionserkennung durch Array

ist, was ich versucht habe:

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

Alle Steine ​​in Spiel werden durch for-Schleife und geht dann zu $ ​​Ziegel-Array. Jeder Stein nach dem Erzeugen erhält obere und linke Position und hat absolute Position. Ich habe versucht zu überprüfen, ob $ ball.t (es ist die Eigenschaften meines Ballobjekts, das die obere Position des Balls erkennt) die Steine ​​erreicht und dann die Steine ​​entfernt.

Danke für jede Hilfe. Ich fange nur an, JS zu lernen, deshalb ist mein Code knotty.

+7

Nur eine Notiz, ist JavaScript nicht erzwingen eine '$ varName'-Konvention wie einige [andere Sprachen] (http://www.php.net). Sie können Ihre Bricks Array 'Bricks' anstelle von' $ Bricks' aufrufen. –

+0

Wenn ich dich richtig verstehe, vergleichst du nur Top-Werte und Links-Werte. Dies bedeutet, dass die beiden Objekte genau die gleichen Werte haben müssen. Ich nehme an, du würdest beim Vergleich mindestens die Breite berücksichtigen müssen, wahrscheinlich auch die Höhe. – bestprogrammerintheworld

+0

Das Problem ist, dass ich die oberste Position meiner Elemente im Array $ bricks nicht erkennen kann. Ich habe versucht durch jQuery-Funktion ** $ Bricks [i] .offset(). Top ** aber Konsole zeigt ** Uncaught TypeError: Objekt 0 hat keine Methode 'Offset' ** – kuzyoy

Antwort

5

Zunächst einmal let'stalk über einige Codefehler

  • $ball.t sollte wahrscheinlich $ball.top
  • Sie nicht brauchen $ als Präfix zu haben, für Ihren Code, es ist einfach eine Variable und Sie Rufen Sie $ball anstelle von ball Hexe Ergebnisse in Annahme Fehler!

für diese Annahme Fehler hier ist das, was man falsch machen:

  • $ball ist ein DOM-Element, kein jQuery Element
  • die gleiche wie $bricks
  • $ball ein Array ist

mit denen aus einigen console.log() geschlossen wir versuchen, den Code zu beheben:

die $ball sollte aufgerufen werden, da es nur eine ist, durch Array-Element ist, wie $ball[0] und weil Sie Variablen zeigt auf DOM-Elemente und nicht jQuery Elemente, müssen Sie es in Jquery wickeln als:

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

eine gute Idee, nicht verwirrt zu werden, ist nur Verwendung $ in jQuery-Elemente, die Vorsilbe in einer Variablen, macht sie nicht zu einem jQuery-Element.

Und Immer wenn Sie sehen, dass Sie einen Fehler haben wie "Element x hat keine Methode y", nehmen Sie immer an, dass Sie eine Methode von einem DOM-Element und nicht von einem jQuery-Element aufrufen.

+0

Dank @balexandre . Es scheint, ich verstehe und $ ($ bricks [i]). Offset(). Top) funktioniert perfekt – kuzyoy

2

Nun, dass @balexandre einige Punkte zu Ihrem Code schön erklärt hat, können wir untersuchen, wie wir die Kollision berechnen können.

Stellen 2 Bereiche einander überlappen (Bereich a teilweise überlappt Bereich b)

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

Der Teil von überlappenden ist | zu | -> 200 bis 300, so dass die Größe der Überlappung 100

Wenn Sie die Zahlen betrachten, beachten Sie, dass die Überlappung wie zu sehen war,

  • Nehmen Sie die kleinere Zahl von der rechten Seite -> 300
  • nehmen sie die greate Nummer der linken Seite -> 200
  • sie voneinander subtrahieren -> 300 - 200 = 100.

Werfen wir einen Blick auf zwei anderen Situationen nehmen. (Bereich b vollständig im Bereich ein)

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

So sind die Werte, die wir haben, sind: Math.min (150,125) //125 für den Endwert und Math.max (50,75) // 75 für den Startwert, was zu einem Wert von 125 bis 75 = 50 für die überlappen

nehmen wir ein das letzte Beispiel aussehen (Bereich ein nicht im Bereich b)

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

die obige Formel ergibt das Ergebnis Math.min (150 , 300) - Math.max (50,200) // -50, die absolutes' Wert der Spalt zwischen den zwei Bereichen, dann 50

Jetzt können wir eine letzte Bedingung hinzufügen, wie Sie die Kollision berechnen wollen, nur Werte > 0 sind von Interesse für uns. Vor diesem Hintergrund können wir es in einen Zustand versetzt.

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

Welche true ergeben, wenn die Überlappung Elemente und false wenn sie es nicht tun.

Angewandt auf Ihren Code, könnten wir die Kollision die folgende Art und Weise berechnen

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

Damit konnten wir true zurück in die Move-Funktion, wenn der Ball mit einem Ziegelstein kollidiert.

Aber hey, wir wollen, dass es in die richtige Richtung abprallen, so dass wir ein anderes Problem. Also eher dann einen Booleschen Wert zurückgibt, ob der Brick kollidiert oder nicht, könnten wir eine neue Richtung, in der Rückkehr wird der Ball bewegen sollte.

Um nur die x oder y Teil der Richtung leicht zu ändern, sollten wir so etwas wie ein Vektor verwendet werden.

so tun, wir 2 Bits eines Integer, wo die Bit b0 Aufenthalte für die x-Richtung und die Bit b1 für die y-Richtung benutzen konnten. So dass.

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 

Aber in der Lage sein, nur einen Teil der Richtung zu ändern, müssen wir die alte Richtung auf die Kollisionsfunktion, und bitweise & und | bzw. sie auszuschalten oder auf

auch verwenden, um übergeben Wir müssen berechnen, von welcher Seite der Ball kollidiert. Glücklicherweise haben wir eine Überlappungsberechnung von vorher, die bereits alle Werte verwendet, die wir brauchen, um die Kollisionsrichtung zu berechnen.

Wenn es darum geht, frome die

  • rechts
    • Brick [ "Right"] - Ball [ "Left"] hat den gleichen Wert wie die Überlappung sein.
  • links
    • Ball [ "Rechts"] - Brick [ "Left"] hat den gleichen Wert wie die Überlappung sein.

Wenn keiner von ihnen wahr ist, hat es entweder kommen aus dem

  • Boden
    • wenn Ball [ "Top"] mehr als (Brick [ "Top" ist ] plus die Hälfte der Brick [ "Höhe"])

oder auch von oben.

Um den Bereich zu reduzieren, wenn die Bedingung, für die Kollision von der Seite, true ausgewertet wir eine weitere Bedingung hinzu, dass die Überlappung als zB ... && overlap < 2

So weniger sein muss, wenn es mit dem Rand kollidiert es doesn nicht immer zur Seite springen.


So genug das Sprechen, im Code könnte dies wie etwas aussehen.

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

Um das zu umgehen, sollten wir auch die moveXX Funktionen ändern, die neue Richtung zu verwenden, zurückgegeben.

Aber wenn wir trotzdem die neue Richtung von der Kollisionsfunktion erhalten werden, könnten wir die vollständige Kollisionserkennung auf die Funktion, bewegen unsere Bewegungsfunktionen zu vereinfachen. Aber vorher sollten wir einen Blick auf die Bewegung Funktionen haben und eine Lookup-Objekt zu field hinzufügen, die die Zahlen für die Richtung hält, um die Lesbarkeit zu gewährleisten.

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

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

Nun sind die Bewegungsfunktionen dann so,

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); 
     } 
... 
} 

wie folgt aussehen könnte, ändert sich die Move-Funktion einfach die Richtung, wenn die Kollisionsfunktion eine Richtung gibt, die von der aktuellen unterscheidet.

Jetzt können wir beginnen, die Wandkollisionen auf die Kollisionsfunktion zu verschieben, um dies zu tun, könnten wir am Anfang einen weiteren Check hinzufügen.

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 

     ... 
} 

Hinweis, verließ ich für die Plattform, um die Kollisionsprüfung heraus, wie die Idee sollte klar sein =)

Hier ist eine Fiddle