はじめに
この「jQuery Flip Puzzle Game」プロジェクトは、JavaScript、jQuery、および Bootstrap に焦点を当てたウェブ開発の実践的な経験を提供します。JavaScript におけるオブジェクト指向プログラミングを含み、ウェブ開発の文脈における「this」バインディングの一般的な問題に対処します。ゲームは、ユーザーインターフェイスに jQuery と Bootstrap 3 を使用して実装されています。Bootstrap に慣れていると役に立ちますが、プロジェクトのコアロジックは jQuery と JavaScript で構築されています。
ゲームでは、プレイヤーはオレンジ色のブロックのグリッドから始めます。各ブロックは 1 面がオレンジ色で、もう 1 面が青い色です。プレイヤーがブロックをクリックすると、その色が反転し、隣接するブロックの色も変化します。ゴールは、すべてのブロックを青に変えてゲームを完了することです。
👀 プレビュー

🎯 タスク
このプロジェクトでは、以下のことを学びます。
- JavaScript におけるオブジェクト指向プログラミングの実装方法と「this」バインディングの問題に対処する方法。
- jQuery と JavaScript を使ってゲームのコアロジックを構築する方法。
- プレイヤーがブロックの色を反転させて勝つインタラクティブなパズルゲームを作成する方法。
🏆 成果
このプロジェクトを完了すると、以下のことができるようになります。
- JavaScript におけるオブジェクト指向プログラミングの原則を適用する。
- JavaScript におけるイベントハンドリングとオブジェクトメソッドに対する「this」バインディングを処理する。
- jQuery を使ってインタラクティブなウェブゲームを開発する。
- Bootstrap 3 を使って視覚的に魅力的でユーザーフレンドリーなインターフェイスを作成する。
基本的な HTML 構造
プレビュー画像に基づいて、初期のウェブページレイアウトを作成します。Bootstrap には、alert ポップアップウィンドウに似た Modal ダイアログがあり、Modal のスタイルは比較的美しいです。
プロジェクトには、Bootstrap の CSS スタイル、自作の CSS スタイル、jQuery ファイル、Bootstrap JavaScript ファイル、およびゲームのメイン JavaScript ファイルを含める必要があります。HTML ページの head タグに次のコードを記述します。
<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">Current level: <b>1</b></div>
</div>
</div>
<div class="aboveGame">
<!-- ゲームボタン -->
<a
class="instruct btn btn-primary"
data-toggle="modal"
data-target="#instructions"
>Game Instructions</a
>
<a
class="newgame btn btn-primary"
data-toggle="modal"
data-target="#newGame"
>Restart</a
>
<a
class="reset btn btn-primary"
data-toggle="modal"
data-target="#restartLevel"
>Reset Level</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>
ゲームプレイ:各正方形は 1 面がオレンジ色で、もう 1
面が青い色です。正方形をクリックすると、その色が反転し、それに隣接する正方形の色も反転します。
</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();
},
// ゲームをリセットする、bind を使って 'this' をリダイレクトする
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; // DOM イベントコールバックの前に 'this' を変数として保存しておくと参照が容易になる
$(".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.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 におけるオブジェクト指向の手法
- JavaScript 内部の this 参照とリダイレクト
- jQuery を用いて DOM を操作する方法
- 行列関係の問題
実際にこのロジックを実装する必要はありませんが、考え方を学ぶ必要があります。問題を解決するためには、まず問題を分析し、関係する論理的な関係を明確にする必要があります。それこそが問題解決の鍵です。



