- @Descripttion:
- @Author: zhangJunQing
- @Date: 2021-04-25
- @LastEditors: zhangJunQing
機會是留給有準備的人的!
這是五一前一周周六,吃了午飯,突然想做個貪吃蛇,沒有為啥,也不知道為啥想做這個,馬上氪:
首先 我們看一下效果圖

我們先將簡單的樣式搞出來:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas貪吃蛇游戲</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100vw;
height: 100vh;
}
#canvas {
background: black;
}
button {
position: absolute;
border: none;
background: #ccc;
color: black;
font-weight: bold;
font-size: 30px;
width: 350px;
height: 80px;
line-height: 80px;
text-align: center;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: all 1s;
}
.but {
top: 20%;
width: 800px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<button>開始游戲</button>
<button class="but">因為打開的視窗大小不是小方塊的倍數,所以可能存在誤差</button>
</body>
</html>
頁面內上只有canvas和兩個按鈕
然后我們需要考慮貪吃蛇這條蛇是怎么運動的,特點是
控制舌頭,蛇身會走舌頭走過的路線,
1 我們需要給canvas定制寬高
2 兩個button顯示隱藏
3 獲取canvas畫筆
4 貪吃蛇分為舌頭和蛇身
let canvas = document.getElementById('canvas')
let btn = document.getElementsByTagName('button')[0]
let btn1 = document.getElementsByTagName('button')[1]
canvas.width = document.getElementsByTagName('body')[0].clientWidth
canvas.height = document.getElementsByTagName('body')[0].clientHeight
let ctx = canvas.getContext('2d')
//蛇頭
let SnackTop = []
//蛇身體
let SnackList = []
//蛇整體
let SnackFonlyList = [...SnackTop, ...SnackList]
創建蛇這個建構式
//X向右 y向下 -x 向左 -y 上
//隨機顏色
let colorList = ["#33B5E5", "#0099CC", "#AA66CC", "#9933CC", "#99CC00", "#669900", "#FFBB33", "#FF8800", "#FF4444", "#CC0000"]
function Snack(x1 = 30, y1 = 30, x2 = 30, y2 = 30, color) {
//這幾個屬性在使用canvas畫方塊的時候會使用
this.x1 = x1; //x軸坐標
this.x2 = x2; //x坐標長度
this.y1 = y1; //y軸左邊
this.y2 = y2; //y坐標長度
this.color = color || colorList[Math.floor(Math.random() * 10)]
this.direction = 'x' //X向右 y向下 -x 向左 -y 上
this.paceSoon = 30
}
創建兩個個蛇身一個舌頭物件陣列,并且讓他從30,30這個坐標開始創建
let x1 = 30, y1 = 30, x2 = 30, y2 = 30;
//生成三個
function getSanck() {
for (let i = 0; i < 3; i++) {
x1 += 30;
SnackList.length != 2 ? SnackList.push(new Snack(x1, y1, x2, y2)) : SnackTop.push(new Snack(x1, y1, x2, y2))
}
}
SnackTop 里面一個Snack實體化物件,SnackList里面兩個
我們是用 +=30 生成的,所以我們需要將整個陣列顛倒,才能是我們整個蛇身的順序,這里好好思考一下
SnackList = SnackList.reverse()
下面要做的就是讓他渲染到頁面上,屬性都有了直接加方法
//創建蛇身和舌頭方法
Snack.prototype.RectFun = function () {
ctx.beginPath();
ctx.fillStyle = this.color
ctx.fillRect(this.x1, this.y1, this.x2, this.y2);
ctx.stroke();
}
讓他動起來的方法,就是一直改變他的x1,y1
Snack.prototype.Update = function () {
if (this.direction == 'x') {
this.x1 += this.paceSoon
} else if (this.direction == 'y') {
this.y1 += this.paceSoon
} else if (this.direction == '-x') {
this.x1 -= this.paceSoon
} else if (this.direction == '-y') {
this.y1 -= this.paceSoon
}
}
通過鍵盤事件改變方向并控制不能直接掉頭
//添加鍵盤按下時間 并且不能直接掉頭
window.addEventListener('keydown', function (e) {
if (e.keyCode == '39') {
SnackTop[0].direction == '-x' ? '' : SnackTop[0].direction = 'x'
// console.log('右')
} else if (e.keyCode == '40') {//下 40
SnackTop[0].direction == '-y' ? '' : SnackTop[0].direction = 'y'
// console.log('下')
} else if (e.keyCode == '37') {//左 37
SnackTop[0].direction == 'x' ? '' : SnackTop[0].direction = '-x'
// console.log('-x')
} else if (e.keyCode == '38') {//右 39
SnackTop[0].direction == 'y' ? '' : SnackTop[0].direction = '-y'
// console.log('-y')
}
})
當然現在還不能實作蛇身移動,再搞一個方法,讓第一個蛇身跟著蛇頭,其他蛇身跟著上一個蛇身,讓他們的狀態一直改變(也就是屬性值改變)
Snack.prototype.directionFun = function (index) {
if (index == 0) {
if (SnackTop[0].direction == 'x') {
this.x1 = SnackTop[0].x1 - 30
this.direction = 'x'
this.y1 = SnackTop[0].y1
} else if (SnackTop[0].direction == 'y') {
this.direction = 'y'
this.x1 = SnackTop[0].x1
this.y1 = SnackTop[0].y1 - 30
} else if (SnackTop[0].direction == '-x') {
this.direction = '-x'
this.x1 = SnackTop[0].x1 + 30
this.y1 = SnackTop[0].y1
} else if (SnackTop[0].direction == '-y') {
this.direction = '-y'
this.y1 = SnackTop[0].y1 + 30
this.x1 = SnackTop[0].x1
}
} else {
if (list[index - 1].direction == 'x') {
this.x1 = list[index - 1].x1
this.y1 = list[index - 1].y1
} else if (list[index - 1].direction == 'y') {
this.x1 = list[index - 1].x1
this.y1 = list[index - 1].y1
} else if (list[index - 1].direction == '-x') {
this.x1 = list[index - 1].x1
this.y1 = list[index - 1].y1
} else if (list[index - 1].direction == '-y') {
this.y1 = list[index - 1].y1
this.x1 = list[index - 1].x1
}
}
}
這里面的list就是上一次每一個蛇身的狀態
我們使用深拷貝進行取狀態
list = JSON.parse(JSON.stringify(SnackList))
這個時候我們的蛇頭和蛇身就可以跟隨運動了
下一步就是食物
我們還是需要給他一個建構式和一個生成食物的方法
//食物
function Eat(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = colorList[Math.floor(Math.random() * 10)]
}
Eat.prototype.RectFunE = function () {
ctx.beginPath();
ctx.fillStyle = this.color
ctx.fillRect(this.x1, this.y1, this.x2, this.y2);
ctx.stroke();
}
我們需要判斷一下當前頁面有沒有食物,沒有得話生成
//食物
if (EatList.length == 0)
EatList.push(new Eat(MathRandomFun(120, canvas.width - 120), MathRandomFun(150, canvas.height - 90), x2, y2))
EatList.map(item => {
item.RectFunE()
})
MathRandomFun方法是生成一個亂數,并且使蛇方塊的大小倍數
//生成食物亂數
function MathRandomFun(min, max) {
let num = Math.floor((max - min) * Math.random() + min)
if (num % 30 == 0) {
return num
} else {
return MathRandomFun(min, max)
}
}
判斷有沒有碰到食物,碰到蛇身加一,添加到陣列,顏色為吃的食物顏色
//判斷舌頭和食物的位置資訊
//SnackTop 蛇頭
//EatList 食物
if (SnackTop[0].x1 == EatList[0].x1 && SnackTop[0].y1 == EatList[0].y1) {
//添加蛇身體
SnackList.push(new Snack(list[list.length - 1].x1, list[list.length - 1].y1, x2, y2, EatList[0].color))
EatList.length = 0
}
設定邊界
//任意一遍超出界限就是游戲失敗
if (SnackTop[0].x1 < -30 || SnackTop[0].x1 > canvas.width || SnackTop[0].y1 < -30 || SnackTop[0].y1 > canvas.height) {
clearInterval(timeID)
// alert('游戲結束')
ctx.clearRect(0, 0, canvas.width, canvas.height)
console.log("=========")
btn.style.opacity = 1
btn1.style.opacity = 1
return false
}
整體代碼如下:
<!--
* @Descripttion:
* @Author: zhangJunQing
* @Date: 2021-04-25
* @LastEditors: zhangJunQing 1
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas貪吃蛇游戲</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100vw;
height: 100vh;
}
#canvas {
background: black;
}
button {
position: absolute;
border: none;
background: #ccc;
color: black;
font-weight: bold;
font-size: 30px;
width: 350px;
height: 80px;
line-height: 80px;
text-align: center;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: all 1s;
}
.but {
top: 20%;
width: 800px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<button>開始游戲</button>
<button class="but">因為打開的視窗大小不是小方塊的倍數,所以可能存在誤差</button>
<script>
let canvas = document.getElementById('canvas')
let btn = document.getElementsByTagName('button')[0]
let btn1 = document.getElementsByTagName('button')[1]
canvas.width = document.getElementsByTagName('body')[0].clientWidth
canvas.height = document.getElementsByTagName('body')[0].clientHeight
let ctx = canvas.getContext('2d')
//蛇頭
let SnackTop = []
//蛇身體
let SnackList = []
//蛇整體
let SnackFonlyList = [...SnackTop, ...SnackList]
//食物陣列
let EatList = []
//存放上次狀態的陣列
let list = []
//定時器id
let timeID = null;
let colorList = ["#33B5E5", "#0099CC", "#AA66CC", "#9933CC", "#99CC00", "#669900", "#FFBB33", "#FF8800", "#FF4444", "#CC0000"]
let x1 = 30, y1 = 30, x2 = 30, y2 = 30;
btn.addEventListener('click', function () {
if (btn.innerHTML == '開始游戲') {
btn.style.opacity = 0
btn1.style.opacity = 0
setTimeout(function () {
requestAnimation()
btn.innerHTML = '觸碰邊界,游戲結束'
timeID = setInterval(requestAnimation, 100)
}, 1000)
}
})
function Snack(x1 = 30, y1 = 30, x2 = 30, y2 = 30, color) {
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
this.color = color || colorList[Math.floor(Math.random() * 10)]
this.direction = 'x' //X向右 y向下 -x 向左 -y 上
this.paceSoon = 30
}
Snack.prototype.RectFun = function () {
ctx.beginPath();
ctx.fillStyle = this.color
ctx.fillRect(this.x1, this.y1, this.x2, this.y2);
ctx.stroke();
}
Snack.prototype.Update = function () {
if (this.direction == 'x') {
this.x1 += this.paceSoon
} else if (this.direction == 'y') {
this.y1 += this.paceSoon
} else if (this.direction == '-x') {
this.x1 -= this.paceSoon
} else if (this.direction == '-y') {
this.y1 -= this.paceSoon
}
}
Snack.prototype.directionFun = function (index) {
if (index == 0) {
if (SnackTop[0].direction == 'x') {
this.x1 = SnackTop[0].x1 - 30
this.direction = 'x'
this.y1 = SnackTop[0].y1
} else if (SnackTop[0].direction == 'y') {
this.direction = 'y'
this.x1 = SnackTop[0].x1
this.y1 = SnackTop[0].y1 - 30
} else if (SnackTop[0].direction == '-x') {
this.direction = '-x'
this.x1 = SnackTop[0].x1 + 30
this.y1 = SnackTop[0].y1
} else if (SnackTop[0].direction == '-y') {
this.direction = '-y'
this.y1 = SnackTop[0].y1 + 30
this.x1 = SnackTop[0].x1
}
} else {
if (list[index - 1].direction == 'x') {
this.x1 = list[index - 1].x1
this.y1 = list[index - 1].y1
} else if (list[index - 1].direction == 'y') {
this.x1 = list[index - 1].x1
this.y1 = list[index - 1].y1
} else if (list[index - 1].direction == '-x') {
this.x1 = list[index - 1].x1
this.y1 = list[index - 1].y1
} else if (list[index - 1].direction == '-y') {
this.y1 = list[index - 1].y1
this.x1 = list[index - 1].x1
}
}
}
//食物
function Eat(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = colorList[Math.floor(Math.random() * 10)]
}
Eat.prototype.RectFunE = function () {
ctx.beginPath();
ctx.fillStyle = this.color
ctx.fillRect(this.x1, this.y1, this.x2, this.y2);
ctx.stroke();
}
//生成八個
function getSanck() {
for (let i = 0; i < 3; i++) {
x1 += 30;
SnackList.length != 2 ? SnackList.push(new Snack(x1, y1, x2, y2)) : SnackTop.push(new Snack(x1, y1, x2, y2))
}
}
getSanck()
//生成食物亂數
function MathRandomFun(min, max) {
let num = Math.floor((max - min) * Math.random() + min)
if (num % 30 == 0) {
return num
} else {
return MathRandomFun(min, max)
}
}
SnackList = SnackList.reverse()
// 生成n個正方形 和一個食物
function requestAnimation() {
list = JSON.parse(JSON.stringify(SnackList))
ctx.clearRect(0, 0, canvas.width, canvas.height)
//食物
if (EatList.length == 0)
EatList.push(new Eat(MathRandomFun(120, canvas.width - 120), MathRandomFun(150, canvas.height - 90), x2, y2))
EatList.map(item => {
item.RectFunE()
})
//判斷舌頭和食物的位置資訊
//SnackTop 舌頭
//EatList 食物
if (SnackTop[0].x1 == EatList[0].x1 && SnackTop[0].y1 == EatList[0].y1) {
//添加蛇身體
SnackList.push(new Snack(list[list.length - 1].x1, list[list.length - 1].y1, x2, y2, EatList[0].color))
EatList.length = 0
}
SnackTop.map(item => {
item.RectFun()
item.Update()
})
SnackList.map((item, index) => {
item.RectFun()
item.directionFun(index)
})
if (SnackTop[0].x1 < -30 || SnackTop[0].x1 > canvas.width || SnackTop[0].y1 < -30 || SnackTop[0].y1 > canvas.height) {
console.log(SnackTop[0], 'SnackTop[0]')
clearInterval(timeID)
// alert('游戲結束')
ctx.clearRect(0, 0, canvas.width, canvas.height)
console.log("=========")
btn.style.opacity = 1
btn1.style.opacity = 1
return false
}
}
//添加鍵盤按下時間 并且不能直接掉頭
window.addEventListener('keydown', function (e) {
if (e.keyCode == '39') {
SnackTop[0].direction == '-x' ? '' : SnackTop[0].direction = 'x'
// console.log('右')
} else if (e.keyCode == '40') {//下 40
SnackTop[0].direction == '-y' ? '' : SnackTop[0].direction = 'y'
// console.log('下')
} else if (e.keyCode == '37') {//左 37
SnackTop[0].direction == 'x' ? '' : SnackTop[0].direction = '-x'
// console.log('-x')
} else if (e.keyCode == '38') {//右 39
SnackTop[0].direction == 'y' ? '' : SnackTop[0].direction = '-y'
// console.log('-y')
}
})
</script>
</body>
</html>
里面的設計思路肯定還有很多種,還有其他思路的大佬歡迎評論,一起探討,一起加油,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/280283.html
標籤:其他
上一篇:【Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2)】Codeforces-1517
下一篇:簡單三子棋 游戲 (C語言實作)
