這個小游戲是一時興起,在暑假時寫的 ,不過當時還比較簡陋,
后來開學后 ,還沒上課,就又拿出來改了改界面,讓它看起來更美觀,更像貪吃蛇一些,
先展示展示幾張圖吧,
開始前

開始咯

吃了些小老鼠后:

撞墻或撞到自己后

當時去做這個時,可能是突然的一個想法,但并不是要做一個完整的游戲出來,
所以這里并沒有設定關卡模式有些細節也并不關注 ,只是去關注了身體移動,吃食物,成長這些主要的問題,
當然,這些主要問題解決,增加其他功能就不是大問題了,
這里我就先說一下我的思路,有興趣和想法的同學們可以自己先去嘗試嘗試,或者可以跟我交流交流,
我的思路
身體怎么移動
這是我第一個思考的問題,
我先想,如果只有一個身體節點,也就是一個小圓 ,應該怎么去移動,控制方向,
當然這個就簡單了,我們先定義一個變數,保存移動方向 , 再定義一個函式,根據方向控制節點的定時移動, 然后監聽鍵盤點擊事件,改變方向就可以了,
那么如果有多個節點呢?顯然每個節點都使用一個函式去控制它移動是不切實際的,
我們可以聯想到火車的節點,轉哪由車頭駕駛員控制,后面跟著跑就行了

那么蛇身體的其他節點也是同樣,它不需要知道方向,它要做的很簡單,就是前一個節點在哪,它走了,我就移動到它的位置就可以了,
那么除了頭部,其他的節點通過一個跟屁股走的函式 就可以完成移動了,
身體怎么成長
成長是在吃到食物后,僅判斷一下頭節點是否于食物節點位置重合即可,
身體成長還是比較簡單的,也就是在尾部增加一個身體節點,但不是一吃到食物就變長,而是原本的最后一個節點移開后再邊長,出現的位置頂替了原最后節點的位置,
怎么判斷死亡
這個問題也是比較簡單的,也是判斷頭部節點位置的問題,主要撞到自己,也就是與身體的某部位的位置重合或者與墻壁的位置重合了,就結束游戲即可,
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 class="snake-node" id="tryNode"></div> -->
</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");
// 自動移動定時器ID
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) {
// 1 : 左 ; 2:上; 3:右 ; 4 : 下
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) {
// 1 : 左 ; 2:上; 3:右 ; 4 : 下
// 如果加速
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
}
// 判斷是否死亡 , 回傳布林值 , true 表示死亡
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 () {
// left 0-960 - 20一個單位 -- 0-58的整數
// top 0-560 - 20 一個單位 -- 0 -28 的整數
// 生成隨機位置
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就可以了,
大家有什么不懂或者有什么高見歡迎評論或私聊,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/298448.html
標籤:其他
