Собираем игру «Найди пару» на HTML и JS
В разных компаниях эта игра называется по-разному: «Мемори», «Найди пару» или «Мемо», но суть одна:
- Есть стопка карточек, где у каждой картинки есть такая же пара.
- Эти карточки раскладываются картинками вниз на игровом поле.
- Игроки по очереди переворачивают любые две карточки.
- Если картинки совпали — игрок забирает их себе и повторяет ход.
- Если не совпали — переворачивает их назад на то же место и ход переходит к другому игроку.
- Побеждает тот, кто соберёт больше пар.
Этим мы сегодня и будем заниматься.
Что делаем
Чтобы проект не превратился в огромный лонгрид, сделаем всё поэтапно. Сегодня будет самое начало:
- мы сделаем игровое поле;
- добавим карточки;
- и настроим механизм самой игры, чтобы их можно было переворачивать и угадывать пары.
Остальное сделаем в другой раз, этого уже будет достаточно для полноценной игры. Для реализации нам понадобятся стандартные для веба инструменты: HTML, CSS и JavaScript. Игра будет работать в браузере, на мобильниках тоже пойдёт, если высота экрана будет 500 пикселей или больше.
Готовим HTML-файл
Чтобы мы могли управлять всеми карточками, будем создавать их в скрипте — так мы сможем сразу добавить их в память и получить над ними полный контроль. Поэтому всё, что у нас будет на странице, — размеченные блоки для будущего контента.
Ещё мы сразу добавим модальное окно с поздравлениями — то, что увидит игрок после победы. Там будет два элемента — поздравительный текст и кнопка с предложением сыграть ещё. Мы всё это скроем с помощью стилей, а пока разместим всё на странице.
- Нормализатор CSS — он сделает так, чтобы страница выглядела одинаково во всех браузерах.
- Библиотека jQuery — с её помощью мы получим доступ ко всем элементам на странице. Можно и без неё, но с ней проще.
- Своя таблица стилей — её мы сделаем позже, пока просто подключим.
Создаём новый файл index.html и добавляем туда сразу весь код страницы:
У нас пока ничего нет из оформления, поэтому пока на странице мы увидим только текст и кнопку:
Настраиваем модальное окно
Раз у нас уже есть окно, его можно сразу настроить — добавить на страницу нужные стили и посмотреть, как будет выглядеть сообщение о победе.Создаём файл style.css и добавляем туда сначала общие настройки для всей страницы:
/* для всех элементов ограничиваем их размеры размерами блока */ * < box-sizing: border-box; >/* общие настройки страницы */ html, body < height: 100%; >/* ставим тёмный фон и растягиваем на всю высоту */ body
Теперь оформим модальное окно:
- Используем блок modal-overlay , чтобы затемнить всю страницу с её содержимым.
- Поверх него в блоке modal покажем наше окно с сообщением о победе.
- Отдельно в стилях настроим внешний вид текста и кнопки.
Добавим этот код в файл со стилями:
/* настройки затемнения при выводе модального окна */ .modal-overlay < /* затемняем экран */ background: rgba(0, 0, 0, 0.8); /* располагаем окно по центру экрана */ position: fixed; top: 0; left: 0; width: 100%; height: 100%; >/* настройки модального окна */ .modal < position: relative; width: 500px; height: 300px; max-height: 90%; max-width: 90%; min-height: 380px; margin: 0 auto; background: white; top: 50%; transform: translateY(-50%); padding: 30px 10px; >/* настройки шрифта сообщения о победе */ .modal .winner < font-size: 80px; text-align: center; color: #4d4d4d; text-shadow: 0px 3px 0 black; >/* если ширина окна маленькая, делаем шрифт поменьше */ @media (max-width: 480px) < .modal .winner < font-size: 60px; >> /* настройки кнопки перезапуска игры */ .modal .restart < margin: 30px auto; padding: 20px 30px; display: block; font-size: 30px; border: none; background: #4d4d4d; background: linear-gradient(#4d4d4d, #222); border: 1px solid #222; border-radius: 5px; color: white; text-shadow: 0px 1px 0 black; cursor: pointer; >/* меняем фон при наведении мышки на кнопку */ .modal .restart:hover < background: linear-gradient(#222, black); >/* выравниваем надписи на модальном окне по центру */ .modal .message
Окно готово, но на старте оно не нужно, поэтому скроем его: добавим display: none; в оба первых блока. Это скроет окно, но всё оформление останется — как только окно нам понадобится, мы уберём это свойство через скрипт.
Создаём карточки
Теперь нам понадобится скрипт. Создаём файл script.js и добавляем в него пустую функцию — вся работа будет происходить внутри неё:
Так как у нас журнал про технологии и программирование, для карточек мы будем использовать логотипы популярных языков, программ и технологий. Для этого сходим в Википедию, возьмём оттуда ссылки на логотипы и используем их для создания массива с карточками. Этот массив, как и всё остальное, положим внутрь нашей основной функции:
// весь скрипт — это одна большая функция (function()< // карточки var cards = [ < // название name: "php", // адрес картинки img: "https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/PHP-logo.svg/297px-PHP-logo.svg.png", // порядковый номер пары id: 1, >, < name: "css3", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/CSS3_logo.svg/160px-CSS3_logo.svg.png", id: 2 >, < name: "html5", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/160px-HTML5_logo_and_wordmark.svg.png", id: 3 >, < name: "jquery", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fd/JQuery-Logo.svg/440px-JQuery-Logo.svg.png", id: 4 >, < name: "javascript", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/99/Unofficial_JavaScript_logo_2.svg/160px-Unofficial_JavaScript_logo_2.svg.png", id: 5 >, < name: "node", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Node.js_logo.svg/262px-Node.js_logo.svg.png", id: 6 >, < name: "photoshop", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/af/Adobe_Photoshop_CC_icon.svg/164px-Adobe_Photoshop_CC_icon.svg.png", id: 7 >, < name: "python", img: "https://www.python.org/static/img/python-logo@2x.png", id: 8 >, < name: "rails", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Ruby_On_Rails_Logo.svg/425px-Ruby_On_Rails_Logo.svg.png", id: 9 >, < name: "sass", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/96/Sass_Logo_Color.svg/213px-Sass_Logo_Color.svg.png", id: 10 >, < name: "sublime", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/7/79/Breezeicons-apps-48-sublime-text.svg/160px-Breezeicons-apps-48-sublime-text.svg.png", id: 11 >, < name: "wordpress", img: "https://upload.wikimedia.org/wikipedia/commons/thumb/2/20/WordPress_logo.svg/440px-WordPress_logo.svg.png", id: 12 >, ]; >)();
На экране пока ничего не появится — мы просто запомнили ссылки на картинки и сложили их в массив. Чтобы сформировать сами карточки, нам понадобится объект — переменная, внутри которой можно объявлять и вызывать разные методы.
Вот что будет происходить в этом объекте:
- Получаем доступ ко всем элементам на странице.
- Виртуально перемешиваем карточки.
- Для каждой карточки динамически создаём HTML-код — с его помощью мы увидим эту карточку на игровом поле.
- Получаем доступ ко всем карточкам.
- Устанавливаем стартовые значения разных свойств и переменных.
- Добавляем всем элементам свою реакцию на нажатие.
- Прописываем логику сравнения выбранной пары.
- Если пары закончились — показываем победное сообщение.
- Если нажали на перезапуск — запускаем игру сначала.
Получается, что за всю логику игры у нас отвечает один объект — мы настраиваем методы и обработку событий, а он дальше сам разбирается, что с этим делать и как реагировать в каждом случае. В этом сила объектно-ориентированного программирования: мы задаём общее поведение, а дальше компьютер решает всё сам.
Для «рубашки» наших карточек возьмём бесплатную фотографию какого-то кода из сервиса Unsplash.
// объявляем объект, внутри которого будет происходить основная механика игры var Memory = < // создаём карточку init: function(cards)< // получаем доступ к классам this.$game = $(".game"); this.$modal = $(".modal"); this.$overlay = $(".modal-overlay"); this.$restartButton = $("button.restart"); // собираем из карточек массив — игровое поле this.cardsArray = $.merge(cards, cards); // перемешиваем карточки this.shuffleCards(this.cardsArray); // и раскладываем их this.setup(); >, // как перемешиваются карточки shuffleCards: function(cardsArray)< // используем встроенный метод .shuffle this.$cards = $(this.shuffle(this.cardsArray)); >, // раскладываем карты setup: function()< // подготавливаем код с карточками на страницу this.html = this.buildHTML(); // добавляем код в блок с игрой this.$game.html(this.html); // получаем доступ к сформированным карточкам this.$memoryCards = $(".card"); // на старте мы не ждём переворота второй карточки this.paused = false; // на старте у нас нет перевёрнутой первой карточки this.guess = null; // добавляем элементам на странице реакции на нажатия this.binding(); >, // как элементы будут реагировать на нажатия binding: function()< // обрабатываем нажатие на карточку this.$memoryCards.on("click", this.cardClicked); // и нажатие на кнопку перезапуска игры this.$restartButton.on("click", $.proxy(this.reset, this)); >, // что происходит при нажатии на карточку cardClicked: function() < // получаем текущее состояние родительской переменной var _ = Memory; // и получаем доступ к карточке, на которую нажали var $card = $(this); // если карточка уже не перевёрнута и мы не нажимаем на ту же самую карточку второй раз подряд if(!_.paused && !$card.find(".inside").hasClass("matched") && !$card.find(".inside").hasClass("picked"))< // переворачиваем её $card.find(".inside").addClass("picked"); // если мы перевернули первую карточку if(!_.guess)< // то пока просто запоминаем её _.guess = $(this).attr("data-id"); // если мы перевернули вторую и она совпадает с первой >else if(_.guess == $(this).attr("data-id") && !$(this).hasClass("picked")) < // оставляем обе на поле перевёрнутыми и показываем анимацию совпадения $(".picked").addClass("matched"); // обнуляем первую карточку _.guess = null; // если вторая не совпадает с первой >else < // обнуляем первую карточку _.guess = null; // не ждём переворота второй карточки _.paused = true; // ждём полсекунды и переворачиваем всё обратно setTimeout(function()< $(".picked").removeClass("picked"); Memory.paused = false; >, 600); > // если мы перевернули все карточки if($(".matched").length == $(".card").length) < // показываем победное сообщение _.win(); >> >, // показываем победное сообщение win: function()< // не ждём переворота карточек this.paused = true; // плавно показываем модальное окно с предложением сыграть ещё setTimeout(function()< Memory.showModal(); Memory.$game.fadeOut(); >, 1000); >, // показываем модальное окно showModal: function()< // плавно делаем блок с сообщением видимым this.$overlay.show(); this.$modal.fadeIn("slow"); >, // прячем модальное окно hideModal: function()< this.$overlay.hide(); this.$modal.hide(); >, // перезапуск игры reset: function()< // прячем модальное окно с поздравлением this.hideModal(); // перемешиваем карточки this.shuffleCards(this.cardsArray); // раскладываем их на поле this.setup(); // показываем игровое поле this.$game.show("slow"); >, // Тасование Фишера–Йетса - https://bost.ocks.org/mike/shuffle/ shuffle: function(array) < var counter = array.length, temp, index; while (counter >0) < index = Math.floor(Math.random() * counter); counter--; temp = array[counter]; array[counter] = array[index]; array[index] = temp; >return array; >, // код, как добавляются карточки на страницу buildHTML: function()< // сюда будем складывать HTML-код var frag = ''; // перебираем все карточки подряд this.$cards.each(function(k, v)< // добавляем HTML-код для очередной карточки frag += '\