🌊 作者主页:海拥
🌊 作者简介:🏆CSDN 全栈畛域优质创作者、🥇HDZ 外围组成员
🌊 粉丝福利:粉丝群 每周送六本书,每月送各种小礼品
应用 javascript 创立游戏是最乏味的学习形式。它会让你放弃能源,这对于学习 Web 开发等简单技能至关重要。此外,你能够和你的敌人一起玩,或者只是向他们展现你做的小东西,他们也会感到很乏味的。在明天的博文中,咱们将应用 HTML、CSS 和 Javascript 创立一个井字游戏。
演示地址:http://haiyong.site/xxoo2
实现 HTML
首先在 head 局部,我将蕴含咱们稍后创立的 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 包装器外部,咱们将有五个局部。
第一局部将只蕴含咱们的题目 h1。
第二局部将显示以后轮到谁。在显示中,咱们有一个蕴含 X 或 O 取决于以后用户的跨度。咱们将类利用于此跨度以对文本进行着色。
第三局部是拿着游戏板的局部。它有一个 container 类,因而咱们能够正确搁置瓷砖。在本节中,咱们有 9 个 div,它们将充当板内的瓷砖。
第四局部将负责颁布最终比赛结果。默认状况下它是空的,咱们将从 javascript 批改它的内容。
最初一部分将保留咱们的控件,其中蕴含一个从新开始按钮。
<main class="background">
<section class="title">
<h1> 井字游戏 </h1>
</section>
<section class="display">
玩家 <span class="display-player playerX">X</span> 的回合
</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"> 从新开始 </button>
</section>
</main>
增加 CSS
我不会具体介绍 CSS 的每一行,但你能够查看源码中的残缺代码。
首先,我将创立 style.css 文件并删除任何浏览器定义的边距和填充,并为整个文档设置我在 HTML 中蕴含的 Google 字体。
* {
padding: 0;
margin: 0;
font-family: 'Itim', cursive;
}
咱们必须增加的下一件重要事件是咱们的板的款式。咱们将应用 CSS 网格来创立板。咱们能够通过为列和行提供 3 倍 33% 的空间将容器一分为二。咱们将通过设置最大宽度和将容器居中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-content
和 align-items 到 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;
}
我应用了两种不同的色彩来更好地区分这两个玩家。为此,我创立两个实用程序类。玩家 X 的色彩为绿色,而玩家 O 的色彩为蓝色。
.playerX {color: #09C372;}
.playerO {color: #498AFB;}
实现 Javascript 局部
因为咱们将 javascript 文件蕴含在<head>
. 这是必须的,因为咱们的脚本将在浏览器解析 HTML 注释之前加载。如果你不想将所有内容都蕴含在此函数中,请随便增加 defer 到脚本标记中或将脚本标记挪动到 body.
window.addEventListener('DOMContentLoaded', () => {});
首先,咱们将保留对 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');
接下来,咱们将增加管制游戏所需的全局变量。咱们将用一个蕴含九个空字符串的数组来初始化一个板。这将保留板上每个图块的 X abd O 值。咱们将有一个 currentPlayer 持有以后回合沉闷的玩家的标记。该 isGameActive 变量将始终为真,直到有人获胜或游戏以平局完结。在这些状况下,咱们会将其设置为 false,以便残余的图块在重置之前处于非活动状态。咱们有三个常数代表游戏完结状态。咱们应用这些常量来防止拼写错误。
let board = ['','', '','', '','', '','', ''];
let currentPlayer = 'X';
let isGameActive = true;
const PLAYERX_WON = 'PLAYERX_WON';
const PLAYERO_WON = 'PLAYERO_WON';
const TIE = 'TIE';
在下一步中,咱们将在棋盘上存储所有获胜的地位。在每个子数组中,咱们将存储能够博得较量的三个地位的索引。所以这 [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 函数中,咱们将决定用户是否想要执行无效的操作。如果 tile 的外部文本是 XorO 咱们返回 false 作为操作有效,否则 tile 为空所以操作无效。
const isValidAction = (tile) => {if (tile.innerText === 'X' || tile.innerText === 'O'){return false;}
return true;
};
下一个效用函数将非常简单。在这个函数中,咱们将接管一个索引作为参数,并将棋盘数组中的相应元素设置为咱们以后玩家的符号。
const updateBoard = (index) => {board[index] = currentPlayer;
}
咱们将编写一个小函数来解决玩家的变动。在这个函数中,咱们将首先从 playerDisplay. 字符串模板文字 player${currentPlayer}将成为 playerX 或 playerO 取决于以后玩家。接下来,咱们将应用三元表达式来更改以后玩家的值。如果是 X,它将是 O 否则它将是 X。当初,咱们扭转了咱们用户的价值,咱们须要更新 innerText 的 playerDisplay,并利用新的播放器类的。
const changePlayer = () => {playerDisplay.classList.remove(`player${currentPlayer}`);
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
playerDisplay.innerText = currentPlayer;
playerDisplay.classList.add(`player${currentPlayer}`);
}
当初咱们将编写发表最终游戏后果的 announer 函数。它将接管完结游戏类型并 innerText 依据后果更新播音员 DOM 节点的。在最初一行中,咱们必须删除暗藏类,因为播音员默认是暗藏的,直到游戏完结。
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');
};
接下来咱们将编写这个我的项目中最乏味的局部之一——后果评估。首先,咱们将创立一个 roundWon 变量并将其初始化为 false。而后咱们将遍历 winConditions 数组并查看棋盘上的每个获胜条件。例如,在第二次迭代中,咱们将查看这些值:board3、board4、board5。
咱们还将进行一些优化,如果任何字段为空,咱们将调用 continue 并跳到下一次迭代,因为如果获胜条件中有空图块,您将无奈获胜。如果所有字段都相等,那么咱们就有一个赢家,因而咱们将 roundWon 设置为 true 并中断 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);
}
接下来咱们将解决用户的操作。此函数将接管一个 tile 和一个索引作为参数。当用户单击一个图块时,将调用此函数。首先咱们须要查看它是否是一个无效的动作,咱们还将查看游戏以后是否处于活动状态。如果两者都为真,咱们 innerText 用以后玩家的符号更新瓷砖的,增加相应的类并更新板阵列。当初所有都更新了,咱们必须查看游戏是否曾经完结,所以咱们调用 handleResultValidation(). 最初,咱们必须调用该 changePlayer 办法将轮次传递给另一个玩家。
const userAction = (tile, index) => {if (isValidAction(tile) && isGameActive) {
tile.innerText = currentPlayer;
tile.classList.add(`player${currentPlayer}`);
updateBoard(index);
handleResultValidation();
changePlayer();}
};
为了让游戏失常运行,咱们必须向磁贴增加事件侦听器。咱们能够通过循环遍历图块数组并为每个图块增加一个事件侦听器来做到这一点。(为了取得更好的性能,咱们只能向容器增加一个事件侦听器并应用事件冒泡来捕捉父级上的磁贴点击,但我认为对于初学者来说这更容易了解。)
tiles.forEach((tile, index) => {tile.addEventListener('click', () => userAction(tile, index));
});
咱们只错过了一项性能:重置游戏。为此,咱们将编写一个 resetBoard 函数。在此函数中,咱们将棋盘设置 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');
});
}
当初咱们只须要将此函数注册为重置按钮的点击事件处理程序。
resetButton.addEventListener('click', resetBoard);
就是这样,咱们有一个功能齐全的井字游戏,你能够和你的敌人一起玩,玩得开心。
🥇 抽粉丝送书啦
《Vue.js 框架与 Web 前端开发从入门到精通》
下方链接可参加抽奖:
http://wallet.hdcj.zhunzha.com/h5/#/jumpMp?t=5AJ4zAM9SDc
写在最初的
作者立志打造一个领有 100 个小游戏的 摸鱼网站,更新进度:41/100
我曾经写了很长一段时间的技术博客,并且次要通过掘金发表,这是我的一篇对于应用 HTML、CSS、JavaScript 创立一个简略的井字游戏。我喜爱通过文章分享技术与高兴。你能够拜访我的博客:https://segmentfault.com/u/haiyong 以理解更多信息。心愿你们会喜爱!😊
💌 欢送大家在评论区提出意见和倡议!💌