文章目录
- 1实现效果
- 2代码实现
1实现效果
游戏难度:简单,一般,困难,噩梦(控制setInterval的time参数)
按钮功能:结束(可以通过修改gameScore的值来修改判定结束的分数),开始,重开
游戏得分
2代码实现
注意路径:
我的img和html文件是并列关系,引入的相对路径
url(./img/xxx.jpg)
资源图片:
mouse.jpg
bg.jpg
hit.jpg
hummer.png
注意:注意计时器,何时打开了,何时需要关闭它,创建的timeId从1开始,之前的不关闭,在创建新的时,timeId是2 ,依次类推,不及时关闭定时器,可能会造成逻辑混乱。
比如:在本例中,在游戏执行过程中,直接修改下拉框后,不执行clearInterval(timeId),而是直接调用startGame(),就会创建另一个定时器,此时如果达到了判输的条件,执行gameOver(),虽然执行了clearInterval(timeId),但是关闭的定时器是新开的那个,此时的timeId值是2,原来的1还存活着。但是这个结束条件依然成立(score<=gameScore),在第一个定时器中每次执行到gameOver()都会被触发,这就是bug,所以要在改变下拉框时,要及时关闭第一个定时器(timeId=1的)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>打地鼠</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: lightblue;
}
.div1 {
text-align: center;
background-color: lightgreen;
width: 100%;
line-height: 50px;
}
.div2 {
margin: 0 auto;
width: 600px;
height: 610px;
}
td {
width: 200px;
height: 200px;
border-radius: 100px;
background-image: url(./img/bg.jpg);
background-size: 100% 100%;
background-repeat: no-repeat;
border: 1px solid #000;
cursor: url("./img/hummer.png"), auto;
}
select{
text-align: center;
width: 70px;
height: 30px;
font-family: 'Courier New', Courier, monospace;
border-radius: 5px;
}
button{
width: 100px;
height: 30px;
font-family: 'Courier New', Courier, monospace;
border-radius: 5px;
background-color: lightgray;
margin-right: 30px;
margin-left: 30px;
}
button:hover{
background-color: orange;
color: #fff;
}
span{
color: red;
width: 40px;
height: 30px;
padding-left: 10px;
font-size: 20px;
font-family: 'Courier New', Courier, monospace;
display: inline-block;
box-sizing: border-box;
}
</style>
</head>
<body>
<audio src="./audio/bg.mp3" class="bgMusic"></audio>
<audio src="./audio/hit.wav" class="hitMusic"></audio>
<div class="div1">
游戏难度:
<select id="select">
<option>简单</option>
<option>一般</option>
<option>困难</option>
<option>噩梦</option>
</select>
<button id="start">开始游戏</button>
游戏得分:
<span id="score">0</span>
</div>
<div class="div2">
<table>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
<script>
//是否开始游戏
let isPlay = false;
let score = 0;
// 是否捶打
let flag = true;
let lastIndex = 0;
var timeId = 0;
let Tid=0;
// 捶了是否锤中
let isRight=false;
let isHit=false;
// 定时器时间
let time = 2000;
let index = 0;
let gameScore = -20;
// 获取元素
let select = document.querySelector("#select");
let start = document.querySelector("#start");
let scoreSpan = document.querySelector("#score");
let tds = document.querySelectorAll("td");
let bgMusic = document.querySelector(".bgMusic");
let hitMusic = document.querySelector(".hitMusic");
// 给开始按钮添加点击事件
start.onclick = function () {
isPlay = !isPlay;
if (isPlay) {
// 获取游戏难度
let level = select.value;
clearTimeout(Tid);
score = 0;
scoreSpan.innerHTML = score;
tds[lastIndex].style.backgroundImage = 'url(./img/bg.jpg)';
// 设置游戏难度
bgMusic.play();
startGame(level);
start.innerHTML = "结束游戏";
} else {
// 结束游戏
bgMusic.pause();
clearInterval(timeId); // 清除定时器
start.innerHTML = "开始游戏";
}
}
// 给td添加点击事件
tds.forEach((td, i) => {
td.onclick = hit.bind(null, i);
})
// 给下拉框添加change事件
select.onchange = function () {
if (isPlay) {
let level = select.value;
flag = true;
score = 0;
scoreSpan.innerHTML = score;
tds[lastIndex].style.backgroundImage = 'url(./img/bg.jpg)';
// 清除之前开启的定时器,直接切换选项,之前开启的定时器还在运行,所以要关闭
clearInterval(timeId);
startGame(level);
}
}
function startGame(level) {
if (level == '简单') {
time = 2000;
} else if (level == '一般') {
time = 1500;
} else if (level == '困难') {
time = 1000;
} else {
time = 800;
}
timeId = setInterval(function () {
// 在还原之前,检查flag的值,为false,代表上一次没有落捶
if (flag == false) {
score -= 5;
scoreSpan.innerHTML = score;
}
// 开始新的出现位置,锤击状态置为未锤击
flag = false;
isRight=false;
isHit=false;
gameOver(score);
// 将上一次的改回原背景
tds[lastIndex].style.backgroundImage = 'url(./img/bg.jpg)';
// 随机获取一个索引
if (isPlay) {
index = Math.floor(Math.random() * tds.length);;
}
// 给当前的索引添加样式
tds[index].style.backgroundImage = 'url(./img/mouse.jpg)';
lastIndex = index;
}, time);
}
// TODO: 游戏是否击中的判断逻辑需要完善
function hit(i) {
// 游戏开始时,锤击才有效
if (isPlay) {
hitMusic.play();
// 锤击的td和随机产生的td索引一致,只第一次捶中有效(锤击地鼠出现位置)
if (i == lastIndex&&!isRight) {
isRight=true;
score += 3;
// 改变背景
tds[i].style.backgroundImage = 'url(./img/hit.jpg)'
} else if(!isHit){
// 捶错地方,只第一次捶错有效
score -= 5;
gameOver(score);
}
scoreSpan.innerHTML = score;
flag = true;
isHit=true;
}
}
// 游戏结束
function gameOver(score) {
if (score <= gameScore) {
// 清楚当前的计时器id
clearInterval(timeId);
scoreSpan.innerHTML = score;
isPlay = false;
flag = true;
start.innerHTML = "重新开始";
Tid = setTimeout(() => {
alert('游戏结束,得分:' + score);
}, 300);
}
}
</script>
</body>
</html>