整体思路
1.设置一个游戏界面main(最外面一圈方框)
2.main内部放置一个容器container,容器非常长,且容器底部位于main的顶部
3.将容器内的黑块和白块事先处理好,并将黑块存储在黑块数组中
容器黑白块处理:①生成一行的黑白块 ②遍历生成一整个容器的黑白块
4.点击开始按钮后,容器开始向下移动,使黑白块随之下落
容器超出main的部分均会被隐藏
5.点击黑块,黑块变白,并清除黑块数组中的值
6.当黑块数组中任意一个黑块触碰到mai底部时,游戏结束
补充:难度不同速度不同,对于速度的控制:
setInterval(moveContainerDown, 20);每隔20毫秒调用下落函数
下落函数里获取当前容器的top值(var top = parseInt(container.style.top) || initialTop;)
再更新容器的top值,使容器向下移动(container.style.top = top + speed + "px";)
改进
我是将所有的黑白块一开始就生成好放在容器里,容器高度设置非常高;
然后让容器整个往下降,超过main的部分隐藏,实现看起来下落的效果,但这样负担较大
理想中应该是容器内的每一行依次下降(而不是容器下降),
最底部一行越界消失,顶部则新增一行,但是我无法实现
完整代码
// bcbk.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>别踩白块</title>
<link rel="stylesheet" href="bcbk.css">
</head>
<body>
<audio id="audioPlayer">
<source src="点击.mp3" type="audio/mpeg">
</audio>
<div id="main">
<div id="container"></div>
</div>
<div class="choose">
<select id="difficulty">
<option value="easy">初级</option>
<option value="medium">中级</option>
<option value="hard">高级</option>
</select>
<button id="startbt">开始游戏</button>
<div id="scoreDisplay">分数: 000</div>
</div>
<script src="bcbk.js"></script>
</body>
</html>
// bcbk.css
* {
margin: 0;
padding: 0;
font-size: 30px;
border-collapse: collapse;
/* 去除单元格之间的间隙 */
box-sizing: border-box;
/* 让边框宽度计入方块尺寸 */
}
/* 最外面那层游戏框 */
#main {
width: 400px;
height: 450px;
border: 1px solid black;
margin: 0 auto;
overflow: hidden;
margin-top: 60px;
position: relative;
}
/* 所有的内部方块构成的整体容器 */
#container {
width: 400px;
position: absolute;
}
/* 选择难度 和 开始按钮 的父盒子 */
.choose {
display: flex;
align-items: center;
/* 子元素垂直居中 */
justify-content: center;
/* 子元素水平居中 */
margin: 10px auto;
width: 400px;
height: 50px;
font-size: 20px;
}
/* 选择难度按钮 */
#difficulty {
margin-right: 10px;
}
/* 分数 */
#scoreDisplay {
margin-left: 10px;
}
/* 方块:默认白色 */
.cell {
width: 100px;
height: 100px;
float: left;
background-color: white;
/* border: 0.1px solid black; */
}
/* 黑块 */
.cellblack {
background-color: black;
}
/* 白块 */
.cellbwhite {
background-color: white;
}
/* 黑块放大动画 */
@keyframes grow {
0% {
transform: scale(1);
opacity: 1;
/* 完全不透明 */
}
100% {
transform: scale(1.4);
/* 放大1.4倍数 */
opacity: 0;
/* 完全透明 */
}
}
// bcbk.js
document.addEventListener('DOMContentLoaded', function () {
//获取容器和主容器元素
var container = document.getElementById("container");
var main = document.getElementById("main");
// 获取开始游戏按钮
// var startButton = document.getElementById("startbt");
var rowCount = 50;//方块行数
var initialTop = -5000;// 初始位置距离页面顶部的高度=rowCount * cell.width
var blackBlocks = [];//存储黑块的数组
var gameStart = false;//游戏开始标志
var intervalID;//定时器
var score;//分数
var scoreDisplay = document.getElementById("scoreDisplay");//分数显示
var audioPlayer = document.getElementById("audioPlayer");//点击音效
//------更新分数
function updateScore() {
score += 5; //点一次黑块加5分
if (score < 10) {
scoreDisplay.textContent = "分数: 00" + score;
}
else if (score < 100) {
scoreDisplay.textContent = "分数: 0" + score;
}
else {
scoreDisplay.textContent = "分数: " + score;
}
//最原始的方法保证分数是三位数,因为肯定玩不到1000分
}
//------点击开始游戏按钮,游戏开始
document.getElementById("startbt").addEventListener("click", function () {
startGame(); // 调用开始游戏函数
});
//------点击黑块则变白,并更新得分
function clickBlackBlock() {
if (this.classList.contains("cellblack")) {
audioPlayer.play();
//添加放大渐变消失动画
this.style.animation = "grow 0.4s forwards";
//将当前元素应用名为 "grow" 的动画,持续0.4秒,并且动画执行完毕后保持最终状态(不回到初始状态)
this.classList.remove("cellblack");//移除该黑块
// this.classList.add("cellwhite");
//之前写的是把黑块变成白块,而不是移除黑块,会出现:
//黑块变白块后,再点击该白块,又变黑块,需要设置clicked棋子,很麻烦,还是直接移除的好
var index = blackBlocks.indexOf(this);
if (index !== -1) {
blackBlocks.splice(index, 1);//清除 已点击黑块 在 blackBlocks中的索引
}
updateScore();//更新分数
}
}
//------生成初始黑块的索引
function generateInitialBlackIndices() {
var indices = [];//存储黑块的索引位置
var blackCount = Math.floor(Math.random() * 3 + 1);//黑块个数1-3(没有4,一行全黑太难了)
for (var i = 0; i < blackCount; i++) {
var index = Math.floor(Math.random() * 4);//黑块索引位置
indices.push(index);
}
return indices;
}
//------生成一行黑白块(包含四个块,四个块里有随机个数的黑块)
function createRow() {
var row = document.createElement("div");//一行方块
row.className = "row";//这行方块的类名叫row
var init = generateInitialBlackIndices();//返回包含了随机生成的初始黑块索引的数组
for (var i = 0; i < 4; i++) {//一行内的四个方块迭代
var cell = document.createElement("div");//小方块
cell.className = "cell";
if (init.includes(i)) {// 是否为黑块索引
cell.classList.add("cellblack");//是黑块索引,就变成黑块
cell.addEventListener("click", clickBlackBlock);//点击黑块变白块
blackBlocks.push(cell);//把黑块加到blackBlocks数组
}
row.appendChild(cell);//将创建的方块元素添加到当前行
}
return row;
}
//------生成所有黑白块
function geneRows() {
for (var i = 0; i < rowCount; i++) {
container.appendChild(createRow());
}
console.log("已生成黑白块");
}
//------重置函数
function resetGame() {
// 清除容器中的所有方块元素
container.innerHTML = '';
score = 0;
var diff = document.getElementById("difficulty");
if (diff.value === "easy") {
speed = 3;
} else if (diff.value === "medium") {
speed = 5;
} else if (diff.value === "hard") {
speed = 7;
}
console.log(speed);
// 重新生成方块行
geneRows();
}
//------开始游戏
function startGame() {
resetGame();
// 将容器移动到初始位置
container.style.top = initialTop + "px";
//------方块下落、判断游戏结束
function moveContainerDown() {
// 获取当前容器的 top 值
var top = parseInt(container.style.top) || initialTop;
// 更新容器的 top 值,使容器向下移动
container.style.top = top + speed + "px";
//获取 main 的底部位置: main 元素顶部距离文档顶部的偏移量加上 main 元素自身的可见高
var mainBottom = main.offsetTop + main.clientHeight;
//遍历所有的黑块,任一黑块的底部>=main的底部,则游戏结束
for (var i = 0; i < blackBlocks.length; i++) {
if (blackBlocks[i].getBoundingClientRect().bottom >= mainBottom + 15) {
//bla...tom 是黑块元素相对于浏览器窗口顶部的距离加上其自身的高度=黑块元素底部距离浏览器窗口顶部的距离
//我也不知道为什么要+15,如果不+,那么黑块离底部还有一段距离的时候就会判断游戏结束
gameOver();
return; //结束函数,避免继续执行下面的代码
}
}
}
//一定要清除之前的定时器,否则速度会叠加,越来越快
clearInterval(intervalID);
//每隔20毫秒调用下落函数,实现容器持续向下移动
intervalID = setInterval(moveContainerDown, 20);
}
//------游戏结束
function gameOver() {
gameStart = false;
var restart = confirm("游戏结束!您的得分是:" + score + " 分。是否重新开始?");
if (restart) {
startGame();
} else {
alert("掰掰");
}
}
});