使用canvas完成貪吃蛇小游戲
- 可以設置速度
- 可以設置有墻或無墻狀態(tài)
-
可以修改蛇身的皮膚
image.png
html代碼
<!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>貪吃蛇</title>
<link rel="stylesheet" href="./index.css">
<script src="http://code.jquery.com/jquery-1.12.4.js"></script>
</head>
<body>
<div class="top">
<div class="left">Snake</div>
<div class="right">Score:<span class="score">0</span></div>
</div>
<canvas id="canvas"></canvas>
<!-- 開始游戲盒子 -->
<div class="begin">
<p>SNAKE</p>
<div class="start">start</div>
<br>
<div class="set">setting</div>
</div>
<!-- 設置盒子 -->
<div class="setBox">
<p>Settings</p>
<div class="start">start</div>
<div class="speed">Speed: <div class="slow">Slow</div>
<div class="normal">Normal</div>
<div class="fast">Fast</div>
</div>
<div class="wall">Wall: <div class="on">On</div>
<div class="off">Off</div>
</div>
</div>
<!-- 結(jié)束盒子 -->
<div class="end">
<p>Game Over</p>
<div class="endText">游戲結(jié)束</div>
<div class="start">start</div>
<br>
<div class="set">setting</div>
</div>
<audio id="audio"
src="https://dl.stream.qqmusic.qq.com/C400000uYx7S4VMZm0.m4a?guid=3943565997&vkey=D44390A154E3DC9A97128581F9652304787C70CB45A7C298ED80BB455C7C21128025EF7F3D3E2F134784118817FB0EE265DB174397269D32&uin=2497208832&fromtag=120002"></audio>
<audio src="./audio/game_over.mp3" id="audio1"></audio>
</body>
<script src="./index.js"></script>
</html>
CSS代碼
@font-face {
font-family: 'VT323';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/vt323/v17/pxiKyp0ihIEF2isfFJA.ttf) format('truetype');
}
* {
margin: 0px;
padding: 0px;
}
a {
text-decoration: none;
color: #000;
}
ul,
ol {
list-style-type: none;
}
table {
border-collapse: collapse;
}
input {
outline: none;
border-width: 1px;
}
textarea {
outline: none;
resize: none;
overflow: auto;
}
.clear {
clear: both;
}
.clear::after {
content: '';
clear: both;
}
body {
background-color: #000;
}
* {
color: #fff;
font-family: "VT323";
}
.top {
margin: 0 auto;
width: 340px;
overflow: hidden;
font-size: 26px;
padding-top: 10px;
margin-bottom: 20px;
}
.top .left {
float: left;
}
.top .right {
float: right;
}
body > div {
width: 340px;
margin: 0 auto;
}
#canvas {
border: 10px solid #fff;
display: block;
margin: 0 auto;
display: none;
}
.begin,
.end {
text-align: center;
}
.end{
display: none;
}
.begin > p,
.end > p {
text-align: center;
font-size: 50px;
padding-top: 30px;
animation: logo-ani 1000ms linear infinite;
margin-bottom: 20px;
}
.begin .start,
.end .start,
.begin .set,
.end .set {
font-size: 26px;
display: inline-block;
line-height: 40px;
cursor: pointer;
}
.begin .start::before,
.end .start::before,
.begin .set::before,
.end .set::before {
content: ">";
padding-right: 10px;
display: none;
}
.begin .start:hover::before,
.end .start:hover::before,
.begin .set:hover::before,
.end .set:hover::before {
display: inline-block;
}
.begin .endText,
.end .endText {
font-size: 30px;
}
@keyframes logo-ani {
50% {
transform: scale(1.3, 1.3);
}
100% {
transform: scale(1, 1);
}
}
.setBox {
text-align: center;
display: none;
}
.setBox > p {
text-align: center;
font-size: 50px;
padding-top: 30px;
margin-bottom: 20px;
}
.setBox .start {
font-size: 26px;
display: inline-block;
line-height: 40px;
cursor: pointer;
padding: 0px !important;
}
.setBox .start::before {
content: ">";
padding-right: 10px;
display: none;
}
.setBox .start:hover::before {
display: inline-block;
}
.setBox > div {
text-align: center;
font-size: 26px;
line-height: 30px;
padding: 5px 0;
}
.setBox > div > div {
display: inline-block;
cursor: pointer;
padding: 0 5px;
}
.setBox > div > .active {
background-color: #fff;
color: #000;
}
js代碼
/*
鍵盤控制
上 下 左 右
*/
//設置畫布的寬高
canvas.width = 640;
canvas.height = 640;
var ctx = canvas.getContext('2d');
//定義有沒有墻
var hasWall = true;
//定義速度
var speed = 60;
//定義一個數(shù)組保存所有的格子
var arr = [];
//定義一個數(shù)組,保存蛇身所有的格子
var snake = [512, 513, 514, 515, 516, 517];
//聲明一個變量 表示當前行進狀態(tài)
var fx = 39; // 37 : 左 , 38 : 上 , 39 : 右 , 40 : 下
//定義一個變量,保存食物 的格子對象
var foods = null;
//定義一個變量,保存分數(shù)
var score = 0;
//定義一個蛇身渲染 和 鍵盤事件的開關(guān)
var flog = false;
//蛇頭坐標
var gird = null;
//給設置墻的按鈕綁定點擊方法
$('.wall>div').click(function () {
var text = $(this).text();
// console.log(text);
switch (text) {
case 'On':
hasWall = true;
break;
case 'Off':
hasWall = false;
break;
}
// console.log(hasWall);
//點擊添加calss名 并刪除兄弟標簽的class名
$(this).addClass('active').siblings().removeClass('active');
})
//模擬讓 on 按鈕被點擊
$('.wall>div').eq(0).click();
//給設置速度的按鈕綁定點擊方法
$('.speed>div').click(function () {
var text = $(this).text();
// console.log(text);
switch (text) {
case 'Slow':
speed = 80;
break;
case 'Normal':
speed = 60;
break;
case 'Fast':
speed = 40;
break;
}
// console.log(hasWall);
//點擊添加calss名 并刪除兄弟標簽的class名
$(this).addClass('active').siblings().removeClass('active');
console.log(speed);
})
//模擬讓 on 按鈕被點擊
$('.speed>div').eq(1).click();
//給set按鈕的點擊方法 游戲設置按鈕
$('.set').click(function () {
//關(guān)掉所有的盒子
$('.begin').hide();
$('.end').hide();
$('#canvas').hide();
//顯示setBox盒子
$('.setBox').show();
})
//定義一個格子的類
function Rect(x, y, n,f) {
this.x = x;
this.y = y;
this.index = n;
// this.c = c || true; //默認為黑色 true 代表黑色 false代表白色
this.fx = f;
}
//添加一個繪制黑色格子的方法
Rect.prototype.drawB = function () {
ctx.beginPath();
ctx.fillStyle = 'block';
ctx.fillRect(this.x, this.y, 20, 20);
ctx.closePath();
ctx.stroke();
}
//添加一個繪制白色格子的方法
Rect.prototype.drawW = function () {
ctx.beginPath();
ctx.fillStyle = '#fff';
ctx.fillRect(this.x, this.y, 20, 20);
ctx.closePath();
ctx.stroke();
}
//添加一個繪制食物的方法
Rect.prototype.drawFood = function () {
ctx.beginPath();
ctx.arc(this.x + 10, this.y + 10, 10, 0, 360)
ctx.fillStyle = 'gold';
ctx.fill();
ctx.closePath();
ctx.stroke();
}
//添加繪制蛇身皮膚的方法
Rect.prototype.drawBody = function (img) {
var that = this;
img.onload = function () {
var deg = Math.PI / 180;
ctx.save();
switch (that.fx) {
case 37:
ctx.translate(that.x, that.y + 20);
ctx.rotate(270 * deg);
break;
case 38:
ctx.translate(that.x, that.y);
ctx.rotate(0);
break;
case 39:
ctx.translate(that.x + 20, that.y);
ctx.rotate(90 * deg);
break;
case 40:
ctx.translate(that.x + 20, that.y + 20);
ctx.rotate(180 * deg);
break;
}
ctx.drawImage(this, 0, 0, 20, 20);
ctx.restore();
}
}
//生成棋盤的方法
function createBox() {
var x = 0;
var y = 0;
arr = [];
for (var i = 0; i < 32 * 32; i++) {
var obj = new Rect(x, y, i, 39);
obj.drawB();
arr.push(obj);
x += 20;
if ((i + 1) % 32 == 0) {
y += 20;
x = 0;
}
}
//設置一下墻
if (hasWall) {
canvas.style.borderColor = '#fff';
} else {
canvas.style.borderColor = '#333';
}
}
//繪制蛇身的方法
function drawSnake() {
for (var i = 0; i < snake.length; i++) {
var a = snake[i];
// if(a == gird){
// arr[a].fx = fx;
// }
//蛇身皮膚
// if (i == snake.length - 1) {
// //執(zhí)行畫頭部的方法
// var img = new Image();
// img.src = './img/1.png';
// arr[a].drawBody(img);
// } else if (i == 0) {
// //執(zhí)行畫尾部的方法
// var img = new Image();
// img.src = './img/3.png';
// arr[a].drawBody(img);
// } else {
// //執(zhí)行畫身體的的方法
// var img = new Image();
// img.src = './img/2.png';
// arr[a].drawBody(img);
// }
//正常小白快蛇身
arr[a].drawW();
}
//
flog = false;
}
//定義一個蛇身移動的方法
function move() {
var timer = setInterval(function () {
canvas.width = canvas.width;
//調(diào)用生成格子的方法
createBox();
//調(diào)用繪制蛇身的方法
drawSnake();
//繪制食物
arr[foods].drawFood();
//判斷蛇是否吃到食物
// eat() 返回值 如果吃到食物返回true 如果沒吃到返回false
if (!eat()) {
//刪掉蛇身數(shù)組中的第一個元素
snake.shift();
}
//根據(jù)行進狀態(tài),改變蛇身數(shù)組
var num = null;
switch (fx) {
case 37://左
num = snake[snake.length - 1] - 1;
break;
case 38://上
num = snake[snake.length - 1] - 32;
break;
case 39://右
num = snake[snake.length - 1] + 1;
break;
case 40://下
num = snake[snake.length - 1] + 32;
break;
}
//碰撞判斷
if (wall(num)) {
//判斷有沒有墻壁
if (hasWall) {
//有墻,撞上就結(jié)束游戲
clearInterval(timer)
// alert('游戲結(jié)束');
end('你個老六,你撞墻了!!!')
audio.pause();
audio1.play();
} else {
//無墻 撞上之后從反方向出現(xiàn)
var head = snake[snake.length - 1];
//右墻
if ((head + 1) % 32 == 0 && fx == 39) {
num = head - 31;
}
//左墻
if (head % 32 == 0 && fx == 37) {
num = head + 31;
}
//上墻
if (head < 32 && fx == 38) {
num = head + 31 * 32;
}
//下墻
if (head >= 32 * 31 && fx == 40) {
num = head - 31 * 32;
}
}
}
//判斷是否咬到自己
if (eatSelf()) {
clearInterval(timer)
audio.pause();
audio1.play();
// alert('游戲結(jié)束');
end('你個老六,咬到自己了!!!')
}
snake.push(num);
// console.log(snake);
}, speed)
}
//定義 碰撞檢測的方法
function wall(n) {
//上下墻的碰撞檢測
if (n < 0 || n > (32 * 32 - 1)) {
return true; //撞到墻壁
}
//蛇頭坐標
var head = snake[snake.length - 1];
//右墻的碰撞檢測
if ((head + 1) % 32 == 0 && n == head + 1) {
return true;
}
//左邊墻壁的碰撞檢測
if (head % 32 == 0 && n == head - 1) {
return true;
}
}
//定義一個生成食物的方法
function food() {
var num = randNum(0, arr.length - 1);
if (snake.indexOf(num) == -1) {
// console.log(arr[num]);
return num;
} else {
return food();
}
}
//判斷蛇吃食物
function eat() {
//獲取蛇頭
var head = snake[snake.length - 1];
if (head == foods) {
//蛇吃到食物
//重新生成食物
foods = food();
arr[foods].drawFood();
score++;
$('.score').text(score);
return true;
} else {
//沒吃到
return false;
}
}
//判斷蛇是否咬到自己
function eatSelf() {
var head = snake[snake.length - 1];
// console.log(snake);
//先將蛇頭從 蛇身數(shù)組中去掉
snake.pop();
// console.log(snake);
if (snake.indexOf(head) == -1) {
snake.push(head);
return false;
} else {
//咬到自己
snake.push(head);
return true;
}
}
//給start按鈕的點擊方法 開始游戲按鈕
$('.start').click(function () {
//關(guān)掉所有的盒子
$('.begin').hide();
$('.end').hide();
$('.setBox').hide();
//顯示canvas盒子
$('#canvas').css('display', 'block');
audio.play();
audio1.pause();
//調(diào)用繪制蛇身的方法
// drawSnake()
//將蛇身初始化
snake = [512, 513, 514, 515, 516];
//清空畫布
canvas.width = canvas.width;
//修改鍵值方向
fx = 39;
//清空分數(shù)
score = 0;
$('.score').text(score);
//調(diào)用生成格子的方法
createBox();
//生成食物
foods = food();
//繪制食物
arr[foods].drawFood();
move();
})
//鍵盤事件
window.onkeydown = function (e) {
var code = e.keyCode;
// 向哪一個方向行進時,不能向反方向移動
if (fx == 39 && code == 37) {
return;
}
if (fx == 37 && code == 39) {
return;
}
if (fx == 38 && code == 40) {
return;
}
if (fx == 40 && code == 38) {
return;
}
//蛇身渲染 和 鍵盤事件的開關(guān)
if (flog) {
return;
}
//如果不是這四個鍵,不執(zhí)行
if (code == 37 || code == 38 || code == 39 || code == 40) {
gird = snake[snake.length - 1];
fx = code;
flog = true;
}
}
//游戲結(jié)束的方法
function end(t) {
//關(guān)掉所有的盒子
$('.begin').hide();
$('#canvas').hide();
$('.setBox').hide();
//顯示end盒子
$('.end').show();
$('.endText').text(t)
}
//封裝隨機數(shù)
function randNum(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
