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

🎯 Задачи
В этом проекте вы научитесь:
- Как реализовать объектно-ориентированное программирование на 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.
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в веб-браузере.
- Эффект страницы выглядит так:

Резюме
При разработке этой игры мы столкнулись с:
- Объектно-ориентированными методами в JavaScript
- Внутренним ссылкой и переадресацией this в JavaScript
- Как манипулировать с DOM с использованием jQuery
- Проблемами с матричными отношениями
На самом деле нам не нужно реализовывать эту логику, но нам нужно приобрести соответствующий мышетак. Чтобы решить проблему, нужно сначала проанализировать ее и прояснить involved логические связи. Именно это является ключом к решению задач.



