If you're seeing this message, it means we're having trouble loading external resources on our website.

Wenn du hinter einem Webfilter bist, stelle sicher, dass die Domänen *. kastatic.org und *. kasandbox.org nicht blockiert sind.

Hauptinhalt

Memory: Ein Raster der Kacheln zeichnen

Im ersten Schritt von "Memory" müssen alle Kacheln zufällig gemischt und in einem rechteckigen Raster ausgelegt werden. Die Oberseite muss nach unten zeigen, damit wir die Bilder auf der anderen Seite jeder Kacheln nicht sehen können.

Kacheln mit der Oberseite nach unten

Zu Beginn der Spielprogrammierung machen wir uns erst mal nur Gedanken über die Kacheln, die mit der Oberseite nach unten zeigen. Um die Bilder kümmern wir uns später.
Die "Kachel" ist ein wichtiges Objekt für das Spiel "Memory". Daher werden wir Objektorientierte Programmierung verwenden, um das Objekt Tile (Kachel) zu definieren und danach mehrer Instanzen davon zu erstellen. Dann können wir mit jeder Kachel sowohl Eigenschaften (wie Koordinate und das Bild) wie auch Methoden (wie z.B. zeichnen) verwenden.
Zu Beginn definieren wir den Konstruktor für Tile. Da wir uns noch nicht um die Bilder kümmern, übergeben wir ihr einfach Parameter für x und y. Wir speichern auch die Grösse (eine Kostante) der Kachel als Eigenschaft des Objektes.
var Tile = function(x, y) {
  this.x = x;
  this.y = y;
  this.size = 50;
};
Nun da wir den Konstruktur definiert haben, können wir ihn in einer Schleife verwenden, um Kacheln an entsprechenden x- und y-Positionen anzulegen. Eigentlich verwenden wir zwei for-Schleifen (eine verschachtelte for-Schleife), da es uns dies einfacher macht, Koordinaten für ein Raster zu generieren.
Zuerst müssen wir ein leeres tiles-Array deklarieren, um alle diese Kacheln zu speichern:
var tiles = [];
Unsere äußere Schleife iteriert so viele Spalten wie wir wollen, unsere innere Schleife iteriert alle Zeilen, und jede neue Tile wird mit einem x und einem y initialisiert, das dieser Zeile und Spalte entspricht.
var NUM_COLS = 5;
var NUM_ROWS = 4;
for (var i = 0; i < NUM_COLS; i++) {
  for (var j = 0; j < NUM_ROWS; j++) {
    var tileX = i * 54 + 5;
    var tileY = j * 54 + 40;
    tiles.push(new Tile(tileX, tileY));
  }
}
Aber es ist schon schwer, herauszufinden, ob die Kacheln gut aussehen, weil wir noch keinen Code haben, um sie zu zeichnen. Vielleicht hätten wir das sogar zuerst machen sollen. Manchmal ist es beim Programmieren schwierig, herauszufinden, was man zuerst machen soll, oder? Fügen wir nun dem Objekt Tile eine Methode hinzu, welche eine Kachel mit der Oberseite nach unten auf den Canvas zeichnet. Wir zeichnen am zugewiesenen Ort ein abgerundetes Rechteck mit einem niedlichen Khan-Blatt auf der Oberseite.
Tile.prototype.draw = function() {
  fill(214, 247, 202);
  strokeWeight(2);
  rect(this.x, this.y, this.size, this.size, 10);
  image(getImage("avatars/leaf-green"),
        this.x, this.y, this.size, this.size);
};
Nun sind wir soweit und können überprüfen, wie unsere Kacheln aussehen. Fügen wir eine neue for-Schleife hinzu, die durch alle Kacheln iteriert und die draw-Methode für sie aufruft.
for (var i = 0; i < tiles.length; i++) {
    tiles[i].draw();
}
So sieht unser Programm mit diesem Code aus. Versuche, ein bisschen an den verschiedenen Zahlen in der verschachtelten for-Schleife herumzuspielen, und schau dir die Veränderungen am Raster oder an ihrem Aussehen (z.B. ein anderes Logo) an.

Kacheln mit der Oberseite nach oben

Wir haben nun ein Raster von nach unten zeigenden Kacheln. Jetzt können wir ein schwierigeres Problem angehen: jedem ein Bild zuzuweisen, so dass es von jedem Bild im Array zwei gibt, die zufällig verteilt liegen. Es gibt sicherlich viele Möglichkeiten, ich würde aber diese hier vorschlagen:
  1. Wir legen mithilfe der Funktion getImage, die zufällige Bilder aus der Bibliothek holt, ein Array von möglichen Bildern an.
  2. Wir brauchen also für unsere 20 Kacheln nur 10 Bilder. Anschließend legen wir ein neues Array mit 2 Kopien von 10 zufällig ausgewählten Bildern aus dem ersten Array an.
  3. Wir verteilen die Bilder im Array zufällig, so dass die Paare im Array nicht mehr direkt nebeneinander liegen.
  4. In der verschachtelten Schleife, in der wir die Kacheln anlegen, weisen wir jeder Kachel ein Bild aus diesem Array zu.
Diese Schritte erscheinen vielleicht noch nicht sinnvoll, aber führen wir sie aus und sehen was passiert.
Schritt 1: Wir legen ein Array von möglichen Bildern an und verwenden dabei die Funktion getImage, um sie aus der Bibliothek auszuwählen.
var faces = [
    getImage("avatars/leafers-seed"),
    getImage("avatars/leafers-seedling"),
    getImage("avatars/leafers-sapling"),
    getImage("avatars/leafers-tree"),
    getImage("avatars/leafers-ultimate"),
    getImage("avatars/marcimus"),
    getImage("avatars/mr-pants"),
    getImage("avatars/mr-pink"),
    getImage("avatars/old-spice-man"),
    getImage("avatars/robot_female_1"),
    getImage("avatars/piceratops-tree"),
    getImage("avatars/orange-juice-squid")
];
Ich habe ein paar Avatare ausgewählt, aber du kannst dir deine eigenen Lieblingsbilder aussuchen. Am wichtigsten ist es, dass dieses Array mindestens 10 Bilder enthält, damit uns bei den 20 Kacheln die Bilder nicht ausgehen. Wir können aber viel mehr als 10 Bilder hinzufügen, um unser Spiel vielfältiger zu machen, wenn eine neue Runde gestartet wird. Die Liste wird ja erst im nächsten Schritt reduziert.
Schritt 2: Weil wir nur 10 Bilder für die Oberseiten unserer 20 Kacheln brauchen, legen wir ein neues Array an, das 2 Kopien von 10 zufällig ausgewählten Bildern aus dem ersten Array enthält.
Dafür legen wir eine for-Schleife an, die 10 Mal durchläuft. Bei jedem Durchlauf wählen wir zufällig einen Index aus dem Array faces für die Oberseiten, übergeben ihn zwei Mal an das Array selected. Wir benutzen dann die Methode splice, um ihn aus dem Array faces zu entfernen und ihn nicht zwei Mal auszuwählen. Dieser letzte Schritt ist sehr wichtig!
var selected = [];
for (var i = 0; i < 10; i++) {
    // Wähle ein Bild zufällig aus dem Array faces
    var randomInd = floor(random(faces.length));
    var face = faces[randomInd];
    // Füge 2 Kopien oben auf dem Array hinzu
    selected.push(face);
    selected.push(face);
    // Entferne das Bild aus dem Array faces damit wir es nicht erneut gewählt wird
    faces.splice(randomInd, 1);
}
Schritt 3: Wir verteilen die Bilder im Array "selected" zufällig, damit die Bildpaare dort nicht mehr nebeneinander liegen.
Du hast wahrscheinlich in deinem Leben schon viele Kartenstapel gemischt, aber hast du jemals ein Array in JavaScript gemischt? Die beliebteste Technik für das "Mischen" in irgendeiner Programmiersprache heißt Fisher-Yates Shuffle, und diese werden wir hier verwenden.
Fisher-Yates Shuffle beginnt mit der Auswahl eines zufälligen Elements im Array, und tauscht dieses mit dem letzten Element im Array aus. Im nächsten Schritt wählt sie ein zufälliges Element aus dem Array neben dem letzten Element aus, und tauscht dieses mit dem vorletzten Element aus. Es geht weiter, bis jedes Element getauscht worden ist.
Du kannst dich durch diese Darstellung klicken, um zu sehen, was ich meine:
Um dies in JavaScript zu implementieren, erstellen wir eine Funktion shuffleArray, die ein Array nimmt und seine Elemente mischt und das dabei das ursprüngliche Array verändert:
var shuffleArray = function(array) {
    var counter = array.length;

    // Solange es Elemente im Array hat
    while (counter > 0) {
        // Wähle einen Zufälligen index
        var ind = Math.floor(Math.random() * counter);
        // Reduziere den Zähler um 1
        counter--;
        // Und tausche das letzte Element damit
        var temp = array[counter];
        array[counter] = array[ind];
        array[ind] = temp;
    }
};
Wenn du den Algorithmus auch nach dem Durcklicken durch die Darstellung und dem Lesen des Codes noch nicht ganz verstehst, versuche es doch einfach mit einem richtigen Kartenstapeloder schau wie watch how Adam Khoury es in dem Youtube Video macht.
Nachdem wir diese Funktion definiert haben, müssen wir sie tatsächlich aufrufen:
shuffleArray(selected);
Und jetzt haben wir ein nach dem Zufall sortiertes Array aus 10 Bildpaaren!
Schritt 4: In der verschachtelten for-Schleife, in der wir die Kacheln anlegen, weisen wir jeder Kachel ein Bild aus diesem Array zu.
Wir haben in unserem Array selected nun 20 Bilder. Wir iterieren also 20 Mal, um neue Instanzen von Kacheln an verschiedenen Orten im Gitter zu erzeugen. Um jeder Kachel ein zufälliges Bild zuzuweisen, rufen wir einfach die Methode pop auf dem Array auf. Diese Methode entfernt das letzte Element aus dem Array und gibt es zurück. Das ist die einfachste Art, um sicherzustellen, dass wir alle Bilder zugewiesen haben, aber keines davon zwei Mal.
for (var i = 0; i < NUM_COLS; i++) {
  for (var j = 0; j < NUM_ROWS; j++) {
    var tileX = i * 54 + 5;
    var tileY = j * 54 + 40;
    var tileFace = selected.pop();
    var tile = new Tile(tileX, tileY, tileFace);
    tiles.push(tile);
  }
}
Hast du bemerkt, wie dieser Code tileFace als dritten Parameter an den Konstruktor von Tile übergibt? Unser Konstruktor hatte ursprünglich nur 2 Parameter, x und y, aber jetzt modifizieren wir ihn so, dass wir auch das Bild der einzelnen Kacheln speichern können. Und ob das Bild nach oben zeigt:
var Tile = function(x, y, face) {
    this.x = x;
    this.y = y;
    this.size = 70;
    this.face = face;
    this.isFaceUp = false;
};
Theoretisch haben wir jetzt also jeder Kachel ein Bild zugewiesen, aber sie werden noch nicht angezeigt. Nun modifizieren wir die Methode Tile.draw damit sie Kacheln mit dem Bild nach oben zeichnen kann:
Tile.prototype.draw = function() {
    fill(214, 247, 202);
    strokeWeight(2);
    rect(this.x, this.y, this.size, this.size, 10);
    if (this.isFaceUp) {
        image(this.face, this.x, this.y,
              this.size, this.size);
    } else {
        image(getImage("avatars/leaf-green"),
              this.x, this.y, this.size, this.size);
    }
};
Um am Ende zu testen, ob alles funktioniert, können wir unsere for-Schleife so anpassen, dass sie die Eigenschaft isFaceUp auf truesetzt bevor gezeichnet wird.
for (var i = 0; i < tiles.length; i++) {
  tiles[i].isFaceUp = true;
  tiles[i].draw();
}
Hier ist das gesamte Programm. Starte es immer wieder neu, um zu sehen, wie sich die Kacheln jedes Mal ändern.

Willst du an der Diskussion teilnehmen?

Noch keine Beiträge.
Verstehst du Englisch? Klick hier, um weitere Diskussionen auf der englischen Khan Academy Seite zu sehen.