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

Ако си зад уеб филтър, моля, увери се, че домейните *. kastatic.org и *. kasandbox.org са разрешени.

Основно съдържание

Игра "Памет": Обръщане на плочките

Добре, сега имаме мрежа от плочки, които можем да показваме с лицето надолу или с лицето нагоре. Но все още не можем да играем играта. Ще ти припомня как работи играта:
  • Когато играта започне, всички плочки са обърнати с лицето надолу.
  • След това играчът обръща 2 плочки и ги избира като кликне върху тях.
  • Ако двете плочки имат еднакво изображение, остават с лицето нагоре. Ако не, трябва да се обърнат обратно с някакво забавяне.

Обръщане на плочките с кликване

В момента имаме програма, която рисува мрежа от плочки и след това не рисува нищо друго. По-нататък ще искаме програмата ни да рисува различни неща – ще започни с рисуване на плочки с лицето надолу, но след това ще покаже кликнатите плочки и ако играчът се справи добре (стискаме палци!), ще покаже екран за победа.
Нека преместим целия си код за чертане в draw функцията на ProcessingJS. Компютърът ще вика draw() непрекъснато, докато програмата се изпълнява, така че плочките ще продължат да се рисуват според това дали са с лицето надолу или нагоре:
draw = function() {
    background(255, 255, 255);
    for (var i = 0; i < tiles.length; i++) {
        tiles[i].draw();
    }
};
Нека обърнем някои от тези плочки с лицето нагоре! За да обърне плочка, играчът трябва да кликне върху нея. За да отвърнем на кликването в една ProcessingJS програма, можем да дефинираме функция mouseClicked и компютърът ще изпълни този код всеки път, когато мишката е кликната.
mouseClicked = function() {
  // обработка на кликването
};
Когато нашата програма забележи, че играчът е кликнал някъде по екрана, искаме да проверим дали е било кликнато върху плочка, като използваме mouseX и mouseY. Да започнем, като добавим метод isUnderMouse към Tile, който връща true, ако дадено x и y са вътре в площта на плочката.
С начина, по който сме нарисували плочките, x и y на плочките отговарят на горния ляв ъгъл на плочката, така че трябва да върнем true само ако даденото x е между this.x и this.x + this.width и ако даденото y е между this.y и this.y + this.width:
Tile.prototype.isUnderMouse = function(x, y) {
    return x >= this.x && x <= this.x + this.width  &&
        y >= this.y && y <= this.y + this.width;
};
След като имаме този метод, можем да използваме for цикъл в mouseClicked, за да проверим дали всяка плочка е под mouseX и mouseY. Ако е така, ще настроим свойството isFaceUp на плочката на true:
mouseClicked = function() {
  for (var i = 0; i < tiles.length; i++) {
    if (tiles[i].isUnderMouse(mouseX, mouseY)) {
      tiles[i].isFaceUp = true;
    }
  }
};
Ето как изглежда това. Кликни върху няколко плочки и виж какво се случва:

Ограничаване на обръщанията на плочката

Забелязваш ли нещо? Имплементирахме един елемент от играта, в който играчът може да обръща плочки, но ни липсва важно ограничение: не трябва да може да обръща повече от 2 плочки на ход.
Трябва по някакъв начин да отбелязваме броя обърнати плочки. Един прост начин би бил да използваме глобална променлива numFlipped, която да увеличаваме всеки път, когато играчът обърне плочка с лицето нагоре. Обръщаме една плочка, само ако numFlipped е по-малко от 2 и плочката не е вече с лицето нагоре:
var numFlipped = 0;
mouseClicked = function() {
    for (var i = 0; i < tiles.length; i++) {
        var tile = tiles[i];
        if (tiles.isUnderMouse(mouseX, mouseY)) {
            if (numFlipped < 2 && !tile.isFaceUp) { 
              tile.isFaceUp = true;
              numFlipped++;
            }
        }
    }
};

Забавяне на обръщането на плочките

Добре, логиката ни за обръщане на две плочки е завършена. Какво следва? Нека отново си припомним правилата на играта:
Ако две плочки имат еднакво изображение, остават с лицето нагоре. В противен случай плочките се обръщат обратно с някакво закъснение.
Нека първо имплементираме втората част, която автоматично обръща плочките, защото ще бъде трудно да проверим първата част, ако не можем лесно да търсим нови съвпадения.
Знаем как да обърнем плочките обратно, като присвоим стойност false на isFaceUp, но как да направим това след някакъв период от време? Във всеки език и среда има различен подход за забавено изпълнение на кода, а ние трябва да разберем как да го направим с ProcessingJS. Трябва ни начин да следим времето – дали периодът на забавяне е минал – и начин да извикаме кода след като времето мине. Ето какво предлагам:
  • Създаваме глобална променлива, наречена delayStartFC, която първоначално има стойност null.
  • Във функцията mouseClicked, точно след като сме обърнали втора плочка, запазваме текущата стойност на frameCount в delayStartFC. Тази стойност ни казва колко кадъра са минали след като програмата ни е започнала да се изпълнява и е един начин да отчитаме време в своите програми.
  • Във функцията draw проверяваме дали новата стойност на frameCount е значително по-голяма от старата и ако е така, обръщаме всички плочки надолу и правим numFlipped равно на 0. Също правим delayStartFC равно на null.
Това всъщност е добро решение, което не изисква много код. Като оптимизация на изпълнението можем да използваме функциите loop и noLoop, за да сме сигурни, че кодът за рисуване в draw се извиква, когато има забавяне. Ето ги и тях:
var numFlipped = 0;
var delayStartFC = null;

mouseClicked = function() {
  for (var i = 0; i < tiles.length; i++) {
    var tile = tiles[i];
    if (tile.isUnderMouse(mouseX, mouseY)) {
      if (numFlipped < 2 && !tile.isFaceUp) {
        tile.isFaceUp = true;
        numFlipped++;
        if (numFlipped === 2) {
          delayStartFC = frameCount;
        }
        loop();
      } 
    }
  }
};

draw = function() {
  if (delayStartFC &&
     (frameCount - delayStartFC) > 30) {
    for (var i = 0; i < tiles.length; i++) {
      tiles[i].isFaceUp = false;
    }
    numFlipped = 0;
    delayStartFC = null;
    noLoop();
  }

  background(255, 255, 255);
  for (var i = 0; i < tiles.length; i++) {
    tiles[i].draw();
  }
};
Обърни няколко плочки долу – не е ли яко как плочките автоматично се обръщат обратно, а? За да успееш да го разбереш по-добре, опитай да промениш дължината на забавянето и това колко плочки трябва да се обърнат преди да започне забавянето.

Проверка на съвпаденията

Ако успя да откриеш две еднакви плочки в упражнението по-горе, сигурно те натъжи това, че се обърнаха обратно с лицето надолу, защото все пак те си съвпаднаха! Сега е време да имплементираме следното правило:
Ако две плочки съвпаднат, те трябва да останат с лицето нагоре.
Това означава, че трябва да проверяваме за еднакви плочки винаги, когато има две обърнати нагоре плочки, и преди да зададем стойност на забавянето. В псевдо код това ще изглежда така:
ако има две плочки, обърнати нагоре:
    ако първата плочка е същата, като втората:
       остави плочките обърнати нагоре
Вече имаме проверка за това дали има две обърнати нагоре плочки (numFlipped === 2), така че как можем да проверим дали плочките имат едно и също лице? Първо ни трябва някакъв начин да достъпим двете обърнати нагоре плочки. Как да открием кои са те?
Бихме могли да обходим масива всеки път и да търсим всички плочки, чието свойство isFaceUp има стойност true, след което да запазим плочката в масив.
Ето един бърз начин: нека винаги да пазим обърнатите плочки в масив, за да можем да ги достъпваме по-лесно. По този начин няма да има нужда да обхождаме целия масив tiles всеки път, когато играчът обърне плочка.
Като първа стъпка, нека да сменим numFlipped с масив и след това да използваме flippedTiles.length навсякъде, където преди използвахме numFlipped. Нашата функция mouseClick изглежда така:
var flippedTiles = [];
var delayStartFC = null;

mouseClicked = function() {
  for (var i = 0; i < tiles.length; i++) {
    var tile = tiles[i];
    if (tile.isUnderMouse(mouseX, mouseY)) {
      if (flippedTiles.length < 2 && !tile.isFaceUp) {
        tile.isFaceUp = true;
        flippedTiles.push(tile);
        if (flippedTiles.length === 2) {
          delayStartFC = frameCount;
          loop();
        }
      } 
    }
  }
};
Сега трябва да разберем дали двете плочки в масива flippedTiles имат едно и също изображение. Добре, какво е свойството им face? Това е обект – а всъщност лицето на съвпадащите плочки трябва да е един и същи обект, тъй като променливата сочи към едно и също място в паметта на компютъра. Това е така, защото създаваме обекта само веднъж (както е при getImage("avatars/old-spice-man"), а след това добавихме същия обект на изображението в масива с лицата два пъти:
var face = possibleFaces[randomInd];
selected.push(face);
selected.push(face);
В JavaScript операторът за равенство ще върне true, ако се използва за две променливи, които сочат към един обект, а тези две променливи реферират към един и същи обект в паметта. Това означава, че нашата проверка може да е проста – използваме оператора за равенство върху свойството face на всяка плочка:
if (flippedTiles[0].face === flippedTiles[1].face) {
  ...
}
След като знаем, че плочките съвпадат, трябва да ги оставим с лицето нагоре. В момента всички те се обръщат с някакво забавяне. В този случай можем просто да не добавяме анимация, но не забравяй, че в по-късните ходове ще има анимация, така че не можем да разчитаме на това.
Вместо това ни трябва начин да разберем че, когато обръщаме всички обратно, трябва да не обръщаме точно тези. Звучи като добра възможност да използваме булево свойство! Нека добавим свойство isMatch към конструктора Tile и да зададем стойност true на isMatch само вътре в if условието:
if (flippedTiles[0].face === flippedTiles[1].face) {
  flippedTiles[0].isMatch = true;
  flippedTiles[1].isMatch = true;
}
Сега можем да използваме това свойство, за да преценим дали да обърнем плочките след забавянето.
for (var i = 0; i < tiles.length; i++) {
  var tile = tiles[i];
  if (!tile.isMatch) {
    tile.isFaceUp = false;
  }
}
Поиграй си с това по-долу! Когато откриеш две еднакви плочки, те трябва да останат с лицето нагоре след забавянето (и след по-нататъшните ходове):

Искаш ли да се присъединиш към разговора?

Все още няма публикации.
Разбираш ли английски? Натисни тук, за да видиш още дискусии в английския сайт на Кан Академия.