を使用して簡単なチックタックトーゲームを作成する


JavaScriptでゲームを作成する最も楽しい方法を学ぶことです.それはあなたのモチベーションを維持し、Web開発のような複雑なスキルを学習するために重要です.さらに、あなたの友人と遊ぶことができるか、ちょうどあなたが作成したものを表示し、彼らは離れて吹き飛ばされるでしょう.今日のブログ記事では、HTML、CSSとJavaScriptを使用してチックタックトーゲームを作成します.

ビデオチュートリアル


あなたが詳細なステップバイステップのビデオを見るならば、あなたは私がこのプロジェクトをカバーしているビデオをチェックすることができます:

HTMLの実装


まず最初に、私たちが後で作成するCSSとJavaScriptファイルを含みます.私はまた、私はこのゲームに最適なフィットだと思うitimと呼ばれるGoogleのフォントを追加します.
    <link rel="stylesheet" href="style.css">
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Itim&display=swap" rel="stylesheet">
    <script src="./index.js"></script>
HTMLの本体はかなり簡単になります.すべてを包むために、私はメインタグを使いますbackground それに.インサイドmain ラッパーは5つのセクションがあります.
最初のセクションではh1 .
番目のセクションでは、そのターンは、現在表示されます.ディスプレイの中には、含まれるスパンがありますX or O 現在のユーザによって異なります.このスパンにクラスを適用してテキストを色分けします.
番目のセクションでは、ゲームのボードを保持するものです.それはcontainer クラスので、適切に我々のタイルを配置することができます.このセクション内では、ボード内のタイルとして動作する9つのdivを持っている.
番目のセクションでは、エンドのゲーム結果を発表する責任があります.デフォルトでは、それは空です、そして、我々はJavaScriptからその内容を変更します.
最後のセクションでは、リセットボタンが含まれているコントロールを保持します.
<main class="background">
        <section class="title">
            <h1>Tic Tac Toe</h1>
        </section>
        <section class="display">
            Player <span class="display-player playerX">X</span>'s turn
        </section>
        <section class="container">
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
        </section>
        <section class="display announcer hide"></section>
        <section class="controls">
            <button id="reset">Reset</button>
        </section>
    </main>

CSSの追加


私はCSSのすべての1行を越えることはありませんが、ビデオを見ることができるか、またはproject's GitHub repository.
最初に私はstyle.css ファイルと任意のブラウザー定義のマージンとパドルを削除し、全体のドキュメントのHTMLに含まれるGoogleフォントを設定します.
* {
    padding: 0;
    margin: 0;
    font-family: 'Itim', cursive;
}
次の重要なことは、我々のボードのスタイルです.我々は、ボードを作成するCSSグリッドを使用します.我々は、3つの33 %のfo rをコラムと行の両方に提供することによって、3に等しく容器を分けることができます.コンテナは真ん中に最大幅を設定して適用しますmargin: 0 auto; .
.container {
    margin: 0 auto;
    display: grid;
    grid-template-columns: 33% 33% 33%;
    grid-template-rows: 33% 33% 33%;
    max-width: 300px;
}
次に、スタイルのタイルをボード内のタイルを追加します.我々は、小さな白い境界線を適用し、最小の幅と高さ100ピクセルを設定します.Flexboxを使用してコンテンツを設定し、justify-contentalign-items to center . 我々はそれに大きなフォントサイズを与え、適用されますcursor: pointer それで、ユーザーはこのフィールドがクリック可能であるということを知っています.
.tile {
    border: 1px solid white;
    min-width: 100px;
    min-height: 100px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 50px;
    cursor: pointer;
}
私は、2人のプレーヤーの間でよりよく区別するために、2つの別々の色を使います.これを行うには2つのユーティリティクラスを作成します.プレーヤーXは緑色の色を持ちますが、プレイヤーOは青い色を持ちます.
.playerX {
    color: #09C372;
}

.playerO {
    color: #498AFB;
}
これらはCSSに関するこのプロジェクトの主要な側面です.これは、プロジェクトが使用するすべてのスタイルではありませんので、project's GitHub repository

JavaScript部分の実装


以来、我々のJavaScriptファイルが含まれて<head> , このイベントハンドラーの間ですべてのコードをラップしなければなりません.HTMLスクリプトがブラウザで解析される前にスクリプトがロードされるので、これが必要です.この機能の中ですべてをラップしたくない場合はdefer スクリプトタグに移動したり、スクリプトタグをbody .
window.addEventListener('DOMContentLoaded', () => {
  // everything goes here
});
まず、DOMノードへの参照を保存します.我々はすべてのタイルを使用してdocument.querySelectorAll() . 配列が欲しいのですが、この関数はnodelistを返します.したがって、Array.from() . また、プレーヤーのディスプレイ、リセットボタンとアナウンサーへの参照をつかむでしょう.
const tiles = Array.from(document.querySelectorAll('.tile'));
const playerDisplay = document.querySelector('.display-player');
const resetButton = document.querySelector('#reset');
const announcer = document.querySelector('.announcer');
次に、我々は我々のゲームを制御するために必要なグローバル変数を追加します.9個の空の文字列を配列で初期化します.これは、ボード上のすべてのタイルのx ABDの値を保持します.私たちはcurrentPlayer それは現在のターンでアクティブなプレーヤーのサインを保持します.The isGameActive 変数がtrueになるまで誰か勝つか、ゲームのネクタイで終了します.残りのタイルがリセットまで不活発になるので、これらのケースで我々はそれをfalseに設定します.エンド・ゲーム状態を表す3つの定数があります.我々は、これらの定数を使用してTyposを避けるために.
let board = ['', '', '', '', '', '', '', '', ''];
let currentPlayer = 'X';
let isGameActive = true;

const PLAYERX_WON = 'PLAYERX_WON';
const PLAYERO_WON = 'PLAYERO_WON';
const TIE = 'TIE';
次のステップでは、ボード上のすべての勝利の位置を格納します.各サブアレイでは、ゲームに勝つことができる3つの位置のインデックスを格納します.だから[0, 1, 2] ウィルは、最初の水平線がプレーヤーによって占められるケースを表します.この配列を使用して、勝者かどうかを決定します.
/*
   Indexes within the board
   [0] [1] [2]
   [3] [4] [5]
   [6] [7] [8]
*/

const winningConditions = [
   [0, 1, 2],
   [3, 4, 5],
   [6, 7, 8],
   [0, 3, 6],
   [1, 4, 7],
   [2, 5, 8],
   [0, 4, 8],
   [2, 4, 6]
];
ここでいくつかのユーティリティ関数を書きます.にisValidAction 関数は、ユーザが有効なアクションを実行したいかどうかを判断します.タイルの内側のテキストがX or O アクションが無効の場合、falseを返します.そうでなければ、タイルは空です.
const isValidAction = (tile) => {
    if (tile.innerText === 'X' || tile.innerText === 'O'){
        return false;
    }

    return true;
};
次のユーティリティ機能は本当に簡単になります.このfucntionでは、パラメータとしてインデックスを受け取り、ボード配列の対応する要素を現在のプレイヤーのサインに設定します.
const updateBoard =  (index) => {
   board[index] = currentPlayer;
}
プレイヤーの変更を処理する少しの関数を書きます.この関数では、現在のPlayerのクラスをplayerDisplay . 文字列リテラルplayer${currentPlayer} どちらかになりますplayerX or playerO 現在のプレーヤーによって異なります.次に、3進式を使用して現在のプレイヤーの値を変更します.あればX であろうO そうでなければX . ユーザーの値を変更したので、更新する必要がありますinnerTextplayerDisplay 新しいプレイヤークラスを適用します.
const changePlayer = () => {
    playerDisplay.classList.remove(`player${currentPlayer}`);
    currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
    playerDisplay.innerText = currentPlayer;
    playerDisplay.classList.add(`player${currentPlayer}`);
}
今、我々は最後のゲーム結果を発表するアナウンス基金を書きます.これは、エンドゲームの種類を受信し、更新innerText 結果に基づいてアナウンサーDOMノードの.最後の行では、ゲームが終了するまでアナウンサーがデフォルトで隠されているので、hideのクラスを削除する必要があります.
const announce = (type) => {
    switch(type){
       case PLAYERO_WON:
            announcer.innerHTML = 'Player <span class="playerO">O</span> Won';
            break;
       case PLAYERX_WON:
            announcer.innerHTML = 'Player <span class="playerX">X</span> Won';
            break;
       case TIE:
            announcer.innerText = 'Tie';
        }
    announcer.classList.remove('hide');
};
次に、このプロジェクトの最も興味深い部分の1つを書きます.まず、ラウンドウォン変数を作成し、falseで初期化します.それから、我々はループを通してループしますwinConditions 配列と各勝利条件のボードを確認します.例えば、2回目の繰り返しでは、これらの値をチェックします.board[3] (a)board[4] (b)board[5] (c)
フィールドのどれかが空ならば、我々はいくらかの最適化をしますcontinue そして、次の繰り返しにスキップします.なぜなら、勝利状態に空のタイルがあれば勝つことはできないからです.すべてのフィールドが等しい場合、我々は勝者を持っているので、我々は真のラウンドウォンを設定し、forループを中断します.
ループの後、私たちはroundWon 変数は、我々は勝者を発表し、非アクティブにゲームを設定します.我々は勝者を持っていない場合は、我々はボード上の空のタイルを持っていると我々は勝者を持っていないと空のタイルが残っていない場合、我々はタイを発表します.
function handleResultValidation() {
  let roundWon = false;
  for (let i = 0; i <= 7; i++) {
    const winCondition = winningConditions[i];
    const a = board[winCondition[0]];
    const b = board[winCondition[1]];
    const c = board[winCondition[2]];
    if (a === "" || b === "" || c === "") {
      continue;
    }
    if (a === b && b === c) {
      roundWon = true;
      break;
    }
  }

  if (roundWon) {
    announce(currentPlayer === "X" ? PLAYERX_WON : PLAYERO_WON);
    isGameActive = false;
    return;
  }

  if (!board.includes("")) announce(TIE);
}
次にユーザの動作を処理します.この関数は、パラメータとしてタイルとインデックスを受け取ります.この関数は、ユーザーがタイルをクリックしたときに呼び出されます.まず、それが有効なアクションかどうかチェックする必要がありますし、我々はまた、ゲームが現在アクティブかどうかチェックします.両方とも真であれば、innerText 現在のプレーヤーの記号を持つタイルの、対応するクラスを追加し、ボードの配列を更新します.すべてが更新される今、我々はゲームが終わったかどうか我々が電話するかどうかチェックしなければなりませんhandleResultValidation() . 最後に私たちはchangePlayer 他のプレイヤーにターンを渡す方法.
const userAction = (tile, index) => {
  if (isValidAction(tile) && isGameActive) {
    tile.innerText = currentPlayer;
    tile.classList.add(`player${currentPlayer}`);
    updateBoard(index);
    handleResultValidation();
    changePlayer();
  }
};
ゲームを取得するには、タイルにイベントリスナーを追加する必要があります.我々は、タイルの配列をループして、それぞれのイベントリスナーを追加することで行うことができます.(より最適なパフォーマンスを得るためには、コンテナに1つのイベントリスナーを追加するだけで、親のタイルクリックをキャプチャするためにイベントバブリングを使用することができましたが、初心者にとっては理解しやすいです).
tiles.forEach( (tile, index) => {
    tile.addEventListener('click', () => userAction(tile, index));
});
私たちが欠場する唯一の機能があります:ゲームをリセットします.その目的のためにresetBoard 関数.この機能では、ボードを9つの空の文字列から構成され、アクティブにゲームを設定すると、アナウンサーを削除し、プレーヤーに戻るX 定義によってX は常に起動します).
最後に我々はしなければならないタイルをループして、空の文字列に戻ってInnertextを設定し、タイルから任意のプレイヤーの特定のクラスを削除することです.
const resetBoard = () => {
    board = ['', '', '', '', '', '', '', '', ''];
    isGameActive = true;
    announcer.classList.add('hide');

    if (currentPlayer === 'O') {
        changePlayer();
    }

    tiles.forEach(tile => {
        tile.innerText = '';
        tile.classList.remove('playerX');
        tile.classList.remove('playerO');
    });
}
今、この機能をリセットボタンのClickイベントハンドラーとして登録するだけです.
resetButton.addEventListener('click', resetBoard);
そして、これは、私たちはあなたの友人と遊ぶことができる楽しい時間を持って完全に機能チックタックトーゲームを持っている.
あなたがどんな点ででも立ち往生するならば、ビデオを見てくださいproject's GitHub repository.
ハッピーハッキング!

どこから、私はより多くを学ぶことができますか?


私はいくつかのプラットフォームでWeb開発をカバーする教育コンテンツを作成する👀 それらをチェックします.
私も、私が私が作成した週または2週の教育内容を共有するニュースレターをつくります.雄牛なし💩 ちょうど教育内容.
🔗 リンク
  • 🍺 サポートフリー教育buy me a beer
  • 💬 参加するcommunity on Discord
  • 📧 ニュースレターSubscribe here
  • 🎥 YouTube
  • 🐦 Twitter
  • 📷 Instagram