这个小游戏是一时兴起。在暑假时写的 。不过当时还比较简陋。 后来开学后 。还没上课。就又拿出来改了改界面。让它看起来更美观,更像贪吃蛇一些。
先展示展示几张图吧。
开始前 开始咯 吃了些小老鼠后: 撞墙或撞到自己后
当时去做这个时,可能是突然的一个想法。但并不是要做一个完整的游戏出来。 所以这里并没有设置关卡模式有些细节也并不关注 。只是去关注了身体移动,吃食物,成长这些主要的问题。 当然,这些主要问题解决,增加其他功能就不是大问题了。
这里我就先说一下我的思路,有兴趣和想法的同学们可以自己先去尝试尝试,或者可以跟我交流交流。
我的思路
身体怎么移动
这是我第一个思考的问题。 我先想,如果只有一个身体节点,也就是一个小圆 。应该怎么去移动。控制方向。 当然这个就简单了。我们先定义一个变量,保存移动方向 。 再定义一个函数,根据方向控制节点的定时移动。 然后监听键盘点击事件。改变方向就可以了。
那么如果有多个节点呢?显然每个节点都使用一个函数去控制它移动是不切实际的。 我们可以联想到火车的节点,转哪由车头驾驶员控制。后面跟着跑就行了
那么蛇身体的其他节点也是同样。它不需要知道方向,它要做的很简单,就是前一个节点在哪,它走了,我就移动到它的位置就可以了。 那么除了头部,其他的节点通过一个跟屁股走的函数 就可以完成移动了。
身体怎么成长
成长是在吃到食物后。仅判断一下头节点是否于食物节点位置重合即可。 身体成长还是比较简单的,也就是在尾部增加一个身体节点,但不是一吃到食物就变长。而是原本的最后一个节点移开后再边长。出现的位置顶替了原最后节点的位置。
怎么判断死亡
这个问题也是比较简单的,也是判断头部节点位置的问题。主要撞到自己,也就是与身体的某部位的位置重合或者与墙壁的位置重合了,就结束游戏即可。
demo
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="shortcut icon" href="./img/favicon.ico" type="image/x-icon">
<title>贪吃蛇</title>
<link rel="stylesheet" href="./greedySnake.css">
</head>
<body>
<div class="wall">
<div class="start" style="display:block">
<div class="tips_con"><text>?</text>
<ul class="tips">
<li><strong>·</strong> 通过键盘的上下左右的键改变贪吃蛇移动的方向</li>
<li><strong>·</strong> 长按 Shift 键可以加速,松开恢复</li>
<li><strong>·</strong> 无关卡设置,触墙或身体会死亡</li>
</ul>
</div>
<button onclick="startGame()">开 始</button>
</div>
<div class="snake" style="display:block">
</div>
<div class="foods"></div>
<div id="infoBar">
<div>得分:<span id="score">0</span></div>
</div>
<audio src="./sounds/eat.wav">播放</audio>
</div>
</body>
<script src="./greedtSnake.js"></script>
</html>```
#### CSS
```css
html,
body {
padding: 0;
margin: 0;
background-color: #111d49;
overflow: hidden;
user-select: none;
}
/* 背景及边框 */
.wall {
width: 980px;
height: 580px;
border: 30px solid transparent;
-webkit-border-image: url(./img/wall.png) 30 30 round;
border-image: url(./img/wall.png) 30 30 round;
margin: 80px auto;
background-color: #666bc6;
position: relative;
}
/* 蛇的身体节点 */
.snake-node {
position: absolute;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
border: 2px solid #010496;
border-radius: 50%;
background-color: #010496;
opacity: 1;
}
/* 提高蛇头层级 */
.snake-node:nth-child(1) {
z-index: 2;
}
/* 眼睛 */
.snake-node:nth-child(1)::before {
content: "";
position: absolute;
top: 0;
left: -5px;
width: 7px;
height: 7px;
background-color: #000;
border: 3px solid #fff;
border-radius: 50%;
z-index: 2;
}
/* 眼睛 */
.snake-node:nth-child(1)::after {
content: "";
position: absolute;
top: 0;
right: -5px;
width: 7px;
height: 7px;
background-color: #000;
border: 3px solid #fff;
border-radius: 50%;
z-index: 2;
}
/* 食物 */
.food {
width: 20px;
height: 20px;
background-image: url(./img/mice.png);
background-size: cover;
position: absolute;
}
/* 开始按钮 */
.start {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
height: 100px;
background-color: rgba(255, 255, 255, 0.692);
transform: translate(-50%, -50%);
text-align: center;
line-height: 100px;
display: none;
}
button {
outline: none;
padding: 5px 10px;
border-radius: 10px;
border: 2px solid #0063bd;
background-image: linear-gradient(to bottom, #62ecfc, #029aea, #0061bc);
color: #fff;
font-size: 16px;
font-weight: 600;
box-shadow: 1px 2px 4px 2px rgba(0, 0, 0, 0.253);
}
button:active {
background-image: linear-gradient(rgba(53, 53, 53, 0.199), rgb(53, 53, 53, 0.2));
}
/* 提示框 */
.tips_con {
position: absolute;
top: 4px;
right: 4px;
width: 22px;
height: 22px;
background-color: rgba(255, 255, 255, 0.704);
border: 1px solid rgb(15, 15, 15);
border-radius: 50%;
text-align: center;
line-height: 22px;
color: #000;
font-weight: bold;
}
.tips_con text {
display: inline-block;
width: 22px;
height: 22px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.704);
text-align: center;
line-height: 22px;
color: #000;
font-weight: bold;
}
.tips_con text:hover {
background-color: #44bcd1;
box-shadow: 0px 0px 0px 1px #44bacf;
}
.tips_con ul {
position: absolute;
top: -24px;
left: 45px;
margin: 0;
padding: 0;
border: 1px solid rgb(0, 0, 0);
background-color: #fff;
width: 200px;
display: none;
}
.tips_con ul li {
list-style-type: none;
padding: 4px 0;
}
.tips_con text:hover+ul {
display: block;
}
/* 底部分数 */
#infoBar {
position: absolute;
bottom: -30px;
left: 50%;
height: 28px;
line-height: 28px;
background-color: rgb(255, 234, 234);
padding: 0px 30px;
text-align: center;
font-size: 17px;
transform: translateX(-50%);
border: 1px solid #fff;
}
/* 音效 */
audio {
visibility: hidden;
display: absolute;
}
最重要的 JavaScript
window.onload = function () {
var start = document.querySelector(".start");
var foods = document.querySelector(".foods");
const Snake = document.querySelector(".snake");
var timer;
const score = document.querySelector("#score");
const audio = document.querySelector("audio");
const maxSpeed = 100, minSpeed = 140
startGame = function () {
setTimeout(() => {
start.style.display = "none";
init();
move(2);
window.addEventListener("keydown", judgeDrec, false);
window.addEventListener("keyup", speedDown, false)
}, 200);
};
var snakeHead
init = function () {
SnakeBodyList = [
[490, 290],
[490, 310],
[490, 330],
];
Snake.innerHTML = ` <div class="snake-node" id="snakeHead"></div>
<div class="snake-node"></div>
<div class="snake-node"></div>`;
snakeHead = document.querySelector("#snakeHead")
Snake.style.display = "block";
addFood();
addFood();
addFood();
addFood();
};
function rotateHead(dire) {
switch (dire) {
case 1: {
snakeHead.style.transform = "translate(-50%,-50%) rotate(-90deg)";
break
}
case 3: {
snakeHead.style.transform = "translate(-50%,-50%) rotate(90deg) ";
break
}
case 4: {
snakeHead.style.transform = "translate(-50%,-50%) rotate(180deg) ";
break
}
default: {
snakeHead.style.transform = " translate(-50%,-50%) rotate(0deg)";
}
}
}
let dire = 2;
let stop = 0;
let speed = minSpeed;
function judgeDrec(e) {
if (e.keyCode == 16) {
if (speed === minSpeed) {
speed = maxSpeed;
move(dire);
stop = 0;
}
return
}
if (stop) {
return;
}
stop = 1;
setTimeout(() => {
stop = 0;
}, speed);
switch (e.keyCode) {
case 37: {
if (dire === 3 || dire === 1) break;
move(1);
dire = 1;
break;
}
case 38: {
if (dire === 4 || dire === 2) break;
move(2);
dire = 2;
break;
}
case 39: {
if (dire === 1 || dire === 3) break;
move(3);
dire = 3;
break;
}
case 40: {
if (dire === 2 || dire === 4) break;
move(4);
dire = 4;
break;
}
}
}
function move(direction) {
rotateHead(direction)
clearInterval(timer);
const moveT = direction === 2 ? -20 : direction === 4 ? 20 : 0;
const moveL = direction === 1 ? -20 : direction === 3 ? 20 : 0;
timer = setInterval(() => {
const left = SnakeBodyList[0][0] + moveL,
top = SnakeBodyList[0][1] + moveT;
if (!ifDeath(top, left))
ifGrowth(top, left);
}, speed);
}
function speedDown(e) {
if (e.keyCode == 16) {
speed = minSpeed
move(dire);
}
return
}
function ifDeath(top, left) {
let end = false
if (top >= 580 || top <= 0 || left <= 0 || left >= 980) {
endGame();
end = true
}
SnakeBodyList.some((v, i) => {
if (i === 0) return false;
if (v[0] === left && v[1] === top) {
endGame();
end = true
return true;
}
return false;
});
return end
}
ifGrowth = function (top, left) {
let ifgrowth = foodPosition.some((v, i) => {
if (v[0] === top - 10 && v[1] === left - 10) {
foodPosition.splice(i, 1);
return true;
}
return false;
});
if (ifgrowth) {
audio.play();
snakeGrowth([left, top], 1);
addFood();
decreaseFood(top - 10, left - 10);
return;
}
snakeGrowth([left, top], 0);
};
var foodPosition = [];
addFood = function () {
const left = Math.round(Math.random() * 48) * 20;
const top = Math.round(Math.random() * 28) * 20;
let isadd = foodPosition.every((v) => {
if (v[0] === top && v[1] === left) {
addFood();
return false;
}
return true;
});
if (isadd) {
foodPosition.push([top, left]);
const food = `<div class="food" id='food${top}${left}' style='left:${left}px;top:${top}px'></div>`;
foods.innerHTML += food;
}
};
decreaseFood = function (top, left) {
score.innerText = score.innerText / 1 + 10;
const id = `food${top}${left}`;
foods.removeChild(document.querySelector("#" + id));
};
var SnakeBodyList = [
[490, 290],
[490, 310],
[490, 330],
];
snakeGrowth = function (newPositipn, add) {
for (let i = 0; i < add; i++) {
SnakeBodyList.push(SnakeBodyList[SnakeBodyList.length - 1]);
let newNode = document.createElement("div")
newNode.setAttribute("class", "snake-node")
Snake.append(newNode)
newNode = null
}
if (newPositipn) {
SnakeBodyList.unshift(newPositipn);
SnakeBodyList.pop();
}
const SnakeList = document.querySelectorAll(".snake-node");
for (let i = 0; i < SnakeBodyList.length; i++) {
SnakeList[i].style.left = `${SnakeBodyList[i][0]}px`;
SnakeList[i].style.top = `${SnakeBodyList[i][1]}px`;
SnakeList[1].style.opacity = 1
}
};
function endGame() {
clearInterval(timer);
alert("游戏结束");
window.removeEventListener("keydown", judgeDrec, false);
Snake.innerHTML = "";
Snake.style.display = "none";
dire = 2
foodPosition = [];
foods.innerHTML = "";
score.innerText = 0;
start.style.display = "block";
}
};
注意
- 这里我设置了吃掉食物后的音效。大家可以去找一个音效的 URL放入即可。不需要可以删掉相关的代码避免报错
- 蛇的眼睛是伪元素,不是图片,墙和老鼠的图片大家搜一搜换一下URL就可以了。
大家有什么不懂或者有什么高见欢迎评论或私聊。
|