Введение

Этот проект, «jQuery Flip Puzzle Game», представляет собой практическое занятие по веб-разработке, сосредоточенное на JavaScript, jQuery и Bootstrap. В нём используется объектно-ориентированное программирование на JavaScript и рассматривается общая проблема связывания «this» в контексте веб-разработки. Игра реализована с использованием jQuery и Bootstrap 3 для пользовательского интерфейса. Хотя знакомство с Bootstrap полезно, основная логика проекта построена на jQuery и JavaScript.

В игре игроки начинают с сетки оранжевых блоков. Каждый блок имеет одну оранжевую сторону и одну синюю сторону. Когда игрок кликает по блоку, его цвет меняется, и цвета соседних блоков также меняются. Цель игры — перевернуть все блоки на синюю, чтобы завершить игру.

👀 Предпросмотр

jQuery Flip Puzzle Game

🎯 Задачи

В этом проекте вы научитесь:

  • Как реализовать объектно-ориентированное программирование на JavaScript и решить проблему связывания «this».
  • Как построить основную логику игры с использованием jQuery и JavaScript.
  • Как создать интерактивную головоломку, в которой игроки переворачивают цвета блоков, чтобы выиграть.

🏆 Достижения

После завершения этого проекта вы сможете:

  • Применять принципы объектно-ориентированного программирования в JavaScript.
  • Управлять связыванием «this» в JavaScript для обработки событий и методов объектов.
  • Разрабатывать интерактивные веб-игры с использованием jQuery.
  • Использовать Bootstrap 3 для создания визуально привлекательных и пользовательских интерфейсов.

Базовая структура HTML

Согласно предварительному изображению создаётся предварительный макет веб-страницы. В Bootstrap есть диалог Modal, который похож на всплывающее окно alert, при этом стиль Modal более эстетичен.

Нам нужно включить в проект CSS-стили из Bootstrap, наши собственные CSS-стили, файл jQuery, файл Bootstrap JavaScript и главный JavaScript-файл игры. Напишите следующий код в теге head HTML-страницы:

<head>
  <meta charset="utf-8" />
  <title>Blue Puzzle</title>
  <!-- Включить Bootstrap CSS -->
  <link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
  />
  <!-- Включить собственный CSS -->
  <link rel="stylesheet" href="style.css" />

  <!-- Включить jQuery и Bootstrap JavaScript -->
  <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
  <!-- Включить главный JavaScript игры -->
  <script src="game.js"></script>
</head>
✨ Проверить решение и практиковаться

Настройка игровой области

Для настройки игровой области, включая кнопки для сброса игры и другие функции, вставьте следующий код в тег body:

<div class="container">
  <div class="heading">
    <h1 class="title">jQuery Flip Puzzle Game</h1>
    <div class="scoresContainer">
      <!-- Отображать текущий уровень игры -->
      <div class="currLevel">Текущий уровень: <b>1</b></div>
    </div>
  </div>
  <div class="aboveGame">
    <!-- Кнопки игры -->
    <a
      class="instruct btn btn-primary"
      data-toggle="modal"
      data-target="#instructions"
      >Инструкция по игре</a
    >
    <a
      class="newgame btn btn-primary"
      data-toggle="modal"
      data-target="#newGame"
      >Перезапустить</a
    >
    <a
      class="reset btn btn-primary"
      data-toggle="modal"
      data-target="#restartLevel"
      >Сбросить уровень</a
    >
  </div>
  <div class="board">
    <!-- Игровая область -->
    <div class="gamerow">
      <div class="gamesquare coord0q0"></div>
    </div>
  </div>
</div>
✨ Проверить решение и практиковаться

Макет всплывающего окна игрового процесса

Отредактируйте содержимое всплывающего окна, соответствующего кнопке "Редактировать инструкцию по игре". Напишите следующий код ниже кода игровой области:

<!-- Всплывающее окно с игровым процессом -->
<div
  class="modal fade"
  id="instructions"
  tabindex="-1"
  role="dialog"
  aria-hidden="true"
>
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Игровой процесс</h4>
      </div>
      <div class="modal-body">
        <p>Как выиграть: Сделайте все пазлы синними.</p>
        <p>
          Игровой процесс: Каждый квадрат имеет одну оранжевую сторону и одну
          синюю сторону. Когда вы кликаете по квадрату, его цвет изменится, и
          цвета соседних квадратов также изменятся.
        </p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" data-dismiss="modal">
          Начать игру
        </button>
      </div>
    </div>
  </div>
</div>
✨ Проверить решение и практиковаться

Макет для модального окна Новая игра

Отредактируйте содержимое всплывающего окна, соответствующего кнопке "Перезапустить". Напишите следующий код ниже кода игровой логики:

<!-- Всплывающее окно для новой игры -->
<div
  class="modal fade"
  id="newGame"
  tabindex="-1"
  role="dialog"
  aria-hidden="true"
>
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Перезапустить</h4>
      </div>
      <div class="modal-body">
        <p>Вы уверены, что хотите перезапустить игру?</p>
      </div>
      <div class="modal-footer">
        <button
          type="button"
          class="btn btn-primary"
          id="newGameConfirm"
          data-dismiss="modal"
        >
          Начать игру
        </button>
      </div>
    </div>
  </div>
</div>
✨ Проверить решение и практиковаться

Макет модального окна подтверждения сброса уровня

Измените содержимое всплывающего окна, соответствующего кнопке "Сбросить уровень". Добавьте следующий код ниже кода "Сбросить уровень":

<!-- Всплывающее окно подтверждения сброса уровня -->
<div
  class="modal fade"
  id="restartLevel"
  tabindex="-1"
  role="dialog"
  aria-hidden="true"
>
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Подтверждение сброса уровня</h4>
      </div>
      <div class="modal-body">
        <p>Вы уверены, что хотите сбросить уровень?</p>
      </div>
      <div class="modal-footer">
        <button
          type="button"
          class="btn btn-primary"
          id="resetLevelConfirm"
          data-dismiss="modal"
        >
          Сбросить
        </button>
      </div>
    </div>
  </div>
</div>
✨ Проверить решение и практиковаться

CSS-стили

CSS-стили игры относительно простые. Код в style.css представлен ниже:

.container {
  width: 600px;
  margin: 0 auto;
}

/* Уровень игры */
.scoresContainer {
  float: right;
  text-align: right;
  font-size: 18px;
}

/* Кнопки игры */
.aboveGame:after {
  display: block;
  margin: 20px 0;
  content: "";
  clear: both;
}

/* Игровая область */
.board {
  position: absolute;
  background-color: #5f5f5f;
  border-radius: 4px;
}

.gamesquare {
  float: left;
  margin-right: 15px;
  border-radius: 3px;
}
✨ Проверить решение и практиковаться

Настройка игровой сцены

Квадратики на игровой панели хранятся в виде двухмерного массива, где каждый элемент имеет значение 0 (установлено в оранжевый цвет) или 1 (установлено в синий цвет). Изначально все значения устанавливаются в 0. Когда на квадратик кликают, его цвет меняется, и определяются координаты соседних квадратиков, чтобы также поменять их цвета, а также меняются значения в двухмерном массиве. Игра завершается, когда все значения в массиве становятся 1 (то есть все квадратики становятся синими).

Основная логика:

  1. Связь между размером игровой сцены, уровнем сложности и размером и количеством квадратиков.

  2. Клик по квадратику и изменение состояния соседних квадратиков.

  3. Проверка, все ли состояния квадратиков равны 1.

function SetStyle() {}
SetStyle.prototype.setGridSize = function (level) {
  var margin = this.getMargin(level);
  var res = ($(".container").width() - margin * level) / level;

  // Устанавливаем размер и расстояние между квадратиками
  $(".gamesquare").css("margin-right", margin);
  $(".gamesquare").css("width", res);
  $(".gamesquare").css("height", res);

  // Устанавливаем высоту, правый отступ и нижний отступ каждой строки
  $(".gamerow").css("height", res);
  $(".gamerow").css("margin-right", margin * -1);
  $(".gamerow").css("margin-bottom", margin);

  // Устанавливаем отступ внутри игровой области
  $(".board").css("padding", margin);
  $(".board").css("padding-bottom", 0);
};
SetStyle.prototype.getMargin = function (level) {
  if (level <= 6) return 15;
  if (level > 15) return 5;
  return 20 - level;
};
✨ Проверить решение и практиковаться

Создать конструктор игры

// Конструктор игры
function Game() {
  // Уровень игры
  this.level = 1;
  // Создаем объекты для управления игрой
  this.gb;
  this.sh = new SetStyle();
}

Конструктор Game предназначен для создания экземпляров игры с свойствами для отслеживания состояния игры и методами для обработки игровых функциональностей, таких как запуск игры, обновление ее состояния и рендеринг графики. Однако фактическая логика для этих функциональностей (внутри методов start, update и render) в настоящее время не предоставлена, и в качестве заготовок оставлены места для будущих реализаций.

✨ Проверить решение и практиковаться

Настройка игровых деталей

Настройте игровые детали в прототипных методах класса Game:

// Прототипный метод класса Game, управляющий конкретной игровой логикой, убедитесь, что сбрасываете ссылку 'this'
Game.prototype = {
  processClick: function (w, h) {
    this.gb.processClick(w, h);
    this.updateCounts();
    if (this.gb.isGameWin()) {
      this.gameEnd();
    }
  },
  // Запустить игру
  beginGame: function () {
    this.setupLevel();
  },
  // Конец игры
  gameEnd: function () {
    this.level++;
    this.resetGame();
  },
  // Сбросить игру, перенаправить 'this' с использованием bind
  resetGame: function () {
    $("#levelDescriptor").html("Enter Level " + this.level);
    setTimeout(
      function () {
        this.setupLevel(); // Когда 'this' не сбрасывается, оно ссылается на объект window
      }.bind(this),
      500
    ); // Используйте bind, чтобы перенаправить 'this' из window в экземпляр
  },
  // Установить уровень сложности
  setupLevel: function () {
    this.gb = new GameBoard(this.level, this.level);
    $(".board").html(""); // Очистить игровую доску
    this.gb.populate(); // Сбросить все квадратики в оранжевый цвет
    this.gb.renderBoard(); // Отрендерить игровую доску и создать квадратики
    this.sh.setGridSize(this.level); // Управить размером квадратиков в игровой области
    this.updateCounts(); // Обновить отображение текущего уровня
    this.applyBindings(); // Переключить цвета квадратиков вокруг кликнутого квадратика
  },
  // Обновить отображение текущего уровня
  updateCounts: function () {
    $(".currLevel").html("Current Level: <b>" + this.level + "</b>");
  },
  applyBindings: function () {
    var that = this; // Сохраните 'this' в переменную перед обратным вызовом DOM-события для удобства ссылки
    $(".gamesquare").click(function () {
      // Получить позицию кликнутого квадратика
      var cname = $(this).attr("class").split(" ")[1];
      var coord = cname.substring(5).split("q");
      var height = parseInt(coord[1]);
      var width = parseInt(coord[0]);
      that.processClick(width, height);
    });
  },
  onNewGameClick: function () {
    this.level = 1;
    this.setupLevel();
  }
};

Этот код расширяет функциональность конструктора Game, добавляя прототипные методы. Эти методы определяют основную игровую логику и взаимодействия.

✨ Проверить решение и практиковаться

Настройка координат блоков

Этот код определяет конструктор-функцию под названием GameBoard, которая используется для создания объектов игровой доски.

// xPos, yPos - координаты блока
function GameBoard(xPos, yPos) {
  // Игровая доска
  // Координаты квадратиков
  this.high = yPos - 1; // Индекс начинается с 0
  this.wide = xPos - 1; // Индекс начинается с 0
  this.count = 0;
  // Горизонтальная координата - wide, вертикальная - high
  //    [0][0] |  [0][1]
  //  - - - - - - - - - - - -
  //    [1][0] |  |[1][1]

  // Создаем двухмерный массив квадратиков
  this.board = new Array(xPos);
  for (var i = 0; i <= this.wide; i++) {
    this.board[i] = new Array(yPos);
  }
}
✨ Проверить решение и практиковаться

Настройка игровых правил

Отрывок кода, который вы предоставили, расширяет функциональность конструктора GameBoard, добавляя прототипные методы, которые определяют основные игровые правила и логику рендеринга.

// Реализация игровых правил
GameBoard.prototype = {
  renderBoard: function () {
    var htmlString = ""; // Структура игровых квадратиков
    for (var j = 0; j <= this.high; j++) {
      htmlString += "<div class='gamerow'>";
      for (var i = 0; i <= this.wide; i++) {
        htmlString += "<div class='gamesquare coord" + i + "q" + j + "'></div>";
      }
      htmlString += "</div>";
    }
    $(".board").html(htmlString);

    for (var i = 0; i <= this.wide; i++) {
      for (var j = 0; j <= this.high; j++) {
        this.processClickView(i, j);
      }
    }
  },
  processClick: function (w, h) {
    //
    // Переворачиваем цвета блоков, окружающих кликнутый блок
    //

    // Находим окружающие блоки, которые нужно перевернуть
    var lowx = w - 1;
    var highx = w + 1;
    var lowy = h - 1;
    var highy = h + 1;

    // Проверяем, является ли кликнутый блок граничным
    if (w == 0) lowx = 0;
    if (w == this.wide) highx = this.wide;
    if (h == 0) lowy = 0;
    if (h == this.high) highy = this.high;

    // Переворачиваем вертикально смежные блоки кликнутого блока
    for (var i = lowy; i <= highy; i++) {
      if (this.board[w][i] == 0) {
        this.board[w][i] = 1;
        this.count++;
      } else {
        this.board[w][i] = 0;
        this.count--;
      }
      this.processClickView(w, i);
    }

    // Переворачиваем горизонтально смежные блоки кликнутого блока
    for (var i = lowx; i <= highx; i++) {
      if (i == w) continue;
      if (this.board[i][h] == 0) {
        this.board[i][h] = 1;
        this.count++;
      } else {
        this.board[i][h] = 0;
        this.count--;
      }
      this.processClickView(i, h);
    }
  },
  // Переворачиваем цвет блока
  processClickView: function (w, h) {
    var coord = ".coord" + w + "q" + h;
    if (this.board[w][h] == 0) {
      $(coord).css("background-color", "#e8BB39");
    } else {
      $(coord).css("background-color", "#6060e0");
    }
  },

  // Сбрасываем все блоки в оранжевый цвет
  populate: function () {
    for (var i = 0; i <= this.wide; i++) {
      for (var j = 0; j <= this.high; j++) {
        this.board[i][j] = 0;
      }
    }
  },

  // Условие победы в игре
  isGameWin: function () {
    return this.count == (this.wide + 1) * (this.high + 1);
  }
};
✨ Проверить решение и практиковаться

Инициализация игры

Этот код настраивает игру при загрузке документа. Он инициализирует игру, запускает первый уровень и также настраивает слушатели событий для сброса текущего уровня или запуска новой игры.

// Инициализировать игру
$(document).ready(function () {
  // Создать игру
  var game = new Game();
  // Начать игру
  game.beginGame();

  // Сбросить квадратики уровня
  $("#resetLevelConfirm").click(function () {
    game.setupLevel();
  });

  // Запустить новую игру
  $("#newGameConfirm").click(function () {
    game.onNewGameClick();
  });
});
✨ Проверить решение и практиковаться

Запуск приложения

  • Откройте index.html в веб-браузере.
    open web
  • Эффект страницы выглядит так:
    Image description
✨ Проверить решение и практиковаться

Резюме

При разработке этой игры мы столкнулись с:

  • Объектно-ориентированными методами в JavaScript
  • Внутренним ссылкой и переадресацией this в JavaScript
  • Как манипулировать с DOM с использованием jQuery
  • Проблемами с матричными отношениями

На самом деле нам не нужно реализовывать эту логику, но нам нужно приобрести соответствующий мышетак. Чтобы решить проблему, нужно сначала проанализировать ее и прояснить involved логические связи. Именно это является ключом к решению задач.