Основно съдържание
Програмиране
Курс: Програмиране > Раздел 4
Урок 5: Създаване на игра за тестване на паметтаИгра "Памет": Рисуване на мрежа от плочки
Първата стъпка от играта на "Памет" е да разбъркаме на случаен принцип всички плочки и след това да ги поставим в правоъгълна решетка с лицето надолу, така че да не можем да видим кое изображение е от другата страна плочката.
Плочки с лицето надолу
За да започнем да програмираме играта, нека се съсредоточим върху създаването на плочки с лицето надолу, и по-късно да измислим как да направим различните изображения.
Плочката е достатъчно важен обект в играта "Памет", за да използваме принципите на обектно-ориентираното програмиране, за да дефинираме тип
Tile
, а след това да създадем няколко инстанции от него. След това ще можем да асоциираме с него свойства (като разположение и име) и поведение (обръщане с лицето нагоре или надолу) за всяка плочка Tile
.За начало ще дефинираме функцията конструктор на
Tile
. Тъй като все още не работим с изображения, ще подадем само аргументите x
и y
. Освен това ще запазим широчината на плочката (която е константа) в свойство на обекта.var Tile = function(x, y) {
this.x = x;
this.y = y;
this.width = 50;
};
След като дефинирахме конструктора, можем да използваме цикъл, за да създадем плочките с подходящи позиции x и y. Всъщност ще използваме два
for
цикъла – вложен for
цикъл – тъй като така ще е по-лесно да генерираме координатите за мрежа.Първо трябва да декларираме празен
tiles
масив, в който да запазим всички тези плочки:var tiles = [];
Външният ни цикъл итерира през колкото колони искаме, вътрешният цикъл итерира през всеки от редовете и всяка нова плочка
Tile
се инициализира с x и y, които отговарят на тези ред и колона.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));
}
}
Но така е трудно да разберем дали плочките ще изглеждат добре, защото все още нямаме код, който да ги нарисува! Всъщност може би трябваше да направим първо това. Понякога в програмирането е трудно да знаем какво трябва да направим по-напред, нали? Сега да добавим метод към обекта
Tile
, който рисува на платното плочка с лицето надолу. Ще нарисуваме закръглен правоъгълник с листото на Кан Академия на зададената позиция.Tile.prototype.draw = function() {
fill(214, 247, 202);
strokeWeight(2);
rect(this.x, this.y, this.width, this.width, 10);
image(getImage("avatars/leaf-green"),
this.x, this.y, this.width, this.width);
};
Толкова сме близо до това да проверим как изглеждат нашите плочки! Нека добавим нов
for
цикъл, който обхожда всички плочки и извиква метода за чертане draw върху тях:for (var i = 0; i < tiles.length; i++) {
tiles[i].draw();
}
Ето как изглежда нашата програма с всичкия код. Опитай да си поиграеш с различните числа във вложения цикъл, за да провериш как ще се промени мрежата или начинът, по който са нарисувани (може би различно лого?).
Плочки с лицето нагоре
Сега, когато имаме мрежа от плочки с лицето надолу, нека да разгледаме един по-труден проблем: трябва да зададем на всяка плочка изображение, такова че в масива да има по 2 изображения от всяко, разпределени на случаен принцип. Вероятно има много начини, по които можем да направим това, но аз предлагам този:
- Създаваме масив от възможните изображения, като използваме функцията
getImage
, за да избираме изображение от библиотеката. - За лицата на нашите 20 плочки ще ни трябват само 10 изображения, затова създаваме масив, който съдържа по 2 копия от 10 случайно избрани изображения от първия масив.
- Разбъркваме избраните изображения в масива, за да може двойките изображения да не стоят едно до друго в масива.
- Във вложения for цикъл, в който създаваме плочките, ще присвоим изображение от масива на всяка плочка.
Така описани тези стъпки може и да нямат смисъл – нека ги направим една по една, за да ги разберем.
Стъпка 1: Създаваме масив от възможните изображения, като използваме функцията
getImage
, за да изберем изображения от нашата библиотека: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")
];
Избрах няколко аватара, но ти можеш да избереш каквито изображения пожелаеш. Важното е да се увериш, че в масива има поне 10 изображения за нашите 20 плочки. Можем да добавим много повече от 10 изображения, за да може играта ни да бъде по-разнообразна всеки път, когато я играем, тъй като в следващата стъпка ще стесним списъка.
Стъпка 2: Ще ни трябват само 10 изображения за лицата на нашите 20 плочки, затова можем да създадем нов масив, който държи по 2 копия от 10 изображения, случайно избрани от първия масив.
За да го направим, създаваме for цикъл, който итерира 10 пъти. Във всяка итерация избираме случаен индекс от масива
faces
, подаваме го 2 пъти в масива selected
, а след това използваме метода splice, за да ги извадим от масива faces
, за да не ги изберем отново. Тази стъпка е много важна!var selected = [];
for (var i = 0; i < 10; i++) {
// Избираме случайно изображение от масива
var randomInd = floor(random(faces.length));
var face = faces[randomInd];
// Подаваме 2 копия в масива
selected.push(face);
selected.push(face);
// Премахваме изображенията от масива с лицата, за да не ги изберем отново
faces.splice(randomInd, 1);
}
Стъпка 3: Разбъркваме избраните изображения в масива, за да може двойките изображения да не стоят едно до друго.
Сигурно ти се е случвало да разбъркваш карти, но едва ли е ставало в JavaScript. Най-популярният начин да разбъркаш нещо във всеки програмен език се нарича разбъркване на Фишер Йейтс и него ще използваме ето тук.
Фишер-Йейтс разбъркването започва като първо избере произволен елемент от масива и го размени с последния елемент в масива. В следващата стъпка избира произволен елемент от масива освен последния елемент и го разменя с предпоследния елемент. Разбъркването продължава, докато не размени всички елементи.
Можеш да си поиграеш с тази визуализация, за да видиш какво имам предвид
За да имплементираме това в JavaScript, нека създадем функция
shuffleArray
, която приема масив, разбърква елементите му и го променя:var shuffleArray = function(array) {
var counter = array.length;
// Докато има елементи в масива
while (counter > 0) {
// Избери произволен индекс
var ind = Math.floor(Math.random() * counter);
// Намали брояча с 1
counter--;
// И размени последния елемент с него
var temp = array[counter];
array[counter] = array[ind];
array[ind] = temp;
}
};
Ако този алгоритъм все още не ти е ясен, след като разгледаш визуализацията и прочетеш кода, можеш да опиташ с истинска колода карти или да видиш как Адам Коури обяснява как се прави в своето видео в YouTube.
След като дефинираме функцията, трябва също и да я извикаме:
shuffleArray(selected);
И сега имаме масив от 10 двойки произволно подредени изображения!
Стъпка 4: Във вложения for цикъл, в който създаваме плочките, ще зададем изображение от масива на всяка плочка.
В нашия масив
selected
имаме 20 изображения, през които итерираме 20 пъти, за да инстанцираме нови плочки на позиции в мрежата. За да зададем всяко изображение на плочка, можем да извикаме метода pop
на масива. Това метод премахва последния елемент от масива и го връща, а това е най-лесният начин да сме сигурни, че задаваме всички изображения, без да ги повтаряме.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);
}
}
Забелязваш ли как този код подава
tileFace
като трети параметър на конструктора Tile
? Нашият конструктор първоначално имаше 2 параметъра, x
и y
, но сега го модифицираме така, че да можем също да запомним и изображението на лицето на всяка плочка, както и дали е с лицето нагоре или не:var Tile = function(x, y, face) {
this.x = x;
this.y = y;
this.width = 70;
this.face = face;
this.isFaceUp = false;
};
Така теоретично имаме изображения, присвоени на всяка плочка, но които все още не показваме! Нека променим метода
Tile.draw
, така че да чертае плочки, които са с лицето нагоре:Tile.prototype.draw = function() {
fill(214, 247, 202);
strokeWeight(2);
rect(this.x, this.y, this.width, this.width, 10);
if (this.isFaceUp) {
image(this.face, this.x, this.y,
this.width, this.width);
} else {
image(getImage("avatars/leaf-green"),
this.x, this.y, this.width, this.width);
}
};
Накрая, за да проверим дали всичко работи, можем да променим своя
for
цикъл и да променим стойността на свойството isFaceUp
на всяка плочка на true
преди да я начертаем:for (var i = 0; i < tiles.length; i++) {
tiles[i].isFaceUp = true;
tiles[i].draw();
}
Ето това е целият код. Опитай да рестартираш, за да видиш как плочките се променят всеки път.
Искаш ли да се присъединиш към разговора?
Все още няма публикации.