基本思想
贪吃蛇的话比较容易,因为刚学习了canvas画布,用来练手。
整体步骤
- 放一个canvas画布并画上几条线,生成格子。(当然 没格子也行)
- 使用数组创建一条蛇,生成食物。
- 监听键盘事件,监控方向。
- 根据方向,计算蛇下一步移动的方向,并控制游戏结束或吃到食物事件的发生。
- 借用计时器不断重绘蛇和食物。
虽然看起来简单,不过其中也有需要注意的地方
1. 食物的生成
食物生成时随机的,可以借助Math.Radom()函数生成。不过需要注意食物不能画在画布外,或者画在蛇的身上。
2. 蛇的移动
蛇移动一步其实就是将蛇的最后一节删除,然后在蛇头的下一步位置添加一节。也就是将存储蛇位置的数组最后一个元素删除,再计算蛇头下一帧的位置再插入到数组的第一位即可。
如果蛇吃到了食物,就不去删除最后一个元素,只在第一位增加元素。
3.蛇移动的方向
蛇是不可以向反方向移动的,同时每一帧只能执行一个转向命令。
所以我将右左方向以1和-1代替,下上方向以2和-2代替。移动时只需要判断当前方向和更改方向之和是否为0即可。
代码
snake.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>Document</title>
<link rel="stylesheet" type="text/css" href="./snake.css">
</head>
<body>
<div class="content">
<canvas id="canvas"></canvas>
</div>
<div class="buttcont">
<button class="beginer" onclick="beginer()">开始</button>
<div id="scoreBox">0</div>
<button class="beginer beginerS" onclick="stoper()">暂停</button>
</div>
</body>
<script type="text/javascript" src="./snake.js.js"></script>
</html>
snake.css
* {
border: 0;
margin: 0;
padding: 0;
}
#scoreBox {
width: 60px;
height: 40px;
margin: 0 auto;
border: 2px solid #f6e58d;
background-color: #f9ca24;
border-radius: 5px;
color: aliceblue;
font-size: 22px;
font-weight: 600;
line-height: 40px;
text-align: center;
}
.content {
margin: 0 auto;
width: 800px;
margin-top: 20px;
border: 5px solid pink;
}
canvas {
display: block;
background-color: bisque;
}
.buttcont {
display: flex;
justify-content: space-around;
width: 800px;
margin: 0 auto;
margin-top: 20px;
}
.beginer {
width: 80px;
height: 50px;
background-color: #badc58;
font-size: 14px;
color: aliceblue;
font-weight: 600;
display: block;
margin: 0 auto;
cursor: pointer;
}
.beginerS{
background-color :#ff7979
}
snake.js
/** @type {HTMLCanvasElement} */
let canvas = document.getElementById('canvas');
let cgn = canvas.getContext('2d');
let scoreBox = document.getElementById('scoreBox')
let isBegin = false;
let score = 0;
// 面板属性
let PANL_WIDTH = canvas.width = 800;
let PANL_HEIGHT = canvas.height = 500;
let CELL_WIDTH = 20;
let MOVE_SPEED = 100;
let GAMEOVER = {
state: false,
reaon: ""
};
// 方向控制 数字为基础 反方向 正负数
let RIGHT_CONTROL = 1;
let LEFT_CONTROL = -1;
let DOWN_CONTROL = 2;
let UP_CONTROL = -2;
let raf; //计时器
let directionNow = -1; //当前方向
let directionRes = -1; //防止短时间内多次转向 出现问题
// 一个蛇的属性
let snakeMes = {
headerColor: '#2ed573',
bodyColor: '#7bed9f',
snakeWidth: CELL_WIDTH,
foodColor: '#ff6b81'
}
let FOOD_POINT = {}; //食物位置
let eatRood = false; //是否吃了一个食物了
// 创建一个蛇 并压入一个头 两个蛇身
let snake = []
reStart();
// 重新开始
function reStart() {
directionNow = directionRes = -1;
clearCanvase();
initSnake();
restDraw();
getFood();
drawFood();
score = 0;
scoreBox.innerText = score;
}
// 调用方法 重绘一些东西
function restDraw() {
// gameMonitor();
clearCanvase();
drawLines();
drawFood();
directionNow = directionRes;
stepMove();
drawSnake();
gameMonitor();
}
// 开始
function beginer() {
if (!isBegin) {
scoreBox.innerText = score;
isBegin = true;
raf = setInterval(restDraw, MOVE_SPEED);
}
if (GAMEOVER.state) {
reStart();
isBegin = true;
raf = setInterval(restDraw, MOVE_SPEED);
GAMEOVER.state = false;
}
}
// 暂停
function stoper() {
if (isBegin) {
isBegin = false;
clearInterval(raf);
}
}
// 初始化蛇
function initSnake() {
snake = [];
for (let i = 0; i < 3; i++) {
snake.push({
x: Math.floor(PANL_WIDTH / 40) * 20 + i * 20,
y: Math.floor(PANL_HEIGHT / 40) * 20
})
}
}
// 监听键盘事件
document.addEventListener('keydown', function (e) {
var event = e || window.event // e:非IE浏览器使用,window.event是IE浏览器使用
let moveDirection;
switch (e.key) {
case 'ArrowRight': {
moveDirection = 1;
} break;
case 'ArrowLeft': {
moveDirection = -1;
} break;
case 'ArrowDown': {
moveDirection = 2;
} break;
case 'ArrowUp': {
moveDirection = -2;
} break;
default:
break;
}
// 判断方向是否是否重合
if (directionNow + moveDirection != 0) {
directionRes = moveDirection;
}
})
// 画一些网格线
function drawLines() {
// 画横线
for (let i = 0; i <= PANL_HEIGHT; i = i + CELL_WIDTH) {
cgn.save();
cgn.beginPath();
cgn.moveTo(0, i);
cgn.lineWidth = 3;
cgn.strokeStyle = '#fff';
cgn.lineTo(PANL_WIDTH, i);
cgn.stroke();
cgn.restore();
}
for (let i = 0; i <= PANL_WIDTH; i = i + CELL_WIDTH) {
cgn.save();
cgn.beginPath();
cgn.moveTo(i, 0);
cgn.lineWidth = 3;
cgn.strokeStyle = '#fff';
cgn.lineTo(i, PANL_HEIGHT);
cgn.stroke();
cgn.restore();
}
}
// 画这个蛇
function drawSnake() {
// 画身体
for (let i = 1; i < snake.length; i++) {
drawSqwar(snake[i].x, snake[i].y, CELL_WIDTH, CELL_WIDTH, snakeMes.bodyColor);
}
// 画蛇头
drawSqwar(snake[0].x, snake[0].y, CELL_WIDTH, CELL_WIDTH, snakeMes.headerColor);
}
// 获得一个食物
function getFood() {
let x = Math.floor(Math.random() * PANL_WIDTH / 20) * 20
let y = Math.floor(Math.random() * PANL_HEIGHT / 20) * 20
let isFood = true;
// 判断食物是不是出现在了蛇身上 出现就重画
while (true) {
snake.forEach(element => {
if (element.x === x && element.y === y) {
x = Math.floor(Math.random() * PANL_WIDTH / 20) * 20
y = Math.floor(Math.random() * PANL_HEIGHT / 20) * 20
isFood = false;
}
});
if (isFood)
break;
else
isFood = true;
}
FOOD_POINT = {
x: x,
y: y
}
// drawSqwar(x, y, CELL_WIDTH, CELL_WIDTH, snakeMes.foodColor);
}
// 画食物
function drawFood() {
drawSqwar(FOOD_POINT.x, FOOD_POINT.y, CELL_WIDTH, CELL_WIDTH, snakeMes.foodColor);
}
// 画一个方块
function drawSqwar(sx, sy, swidth, sheight, scolor) {
cgn.save();
cgn.beginPath();
cgn.fillStyle = scolor;
cgn.fillRect(sx, sy, swidth, sheight);
cgn.restore();
}
// 蛇的正常移动
function stepMove() {
if (!eatRood) {
snake.splice(snake.length - 1, 1);
}
eatRood = false;
let head = snake[0];
let newhead = {}
// 正常
if (directionNow == 1 || directionNow == -1) {
newhead.x = head.x + directionNow / 1 * CELL_WIDTH;
newhead.y = head.y;
} else if (directionNow == 2 || directionNow == -2) {
newhead.x = head.x;
newhead.y = head.y + directionNow / 2 * CELL_WIDTH;
} else { }
// 穿墙
if (newhead.x >= PANL_WIDTH)
newhead.x = 0;
if (newhead.x < 0)
newhead.x = PANL_WIDTH - CELL_WIDTH;
if (newhead.y >= PANL_HEIGHT)
newhead.y = 0;
if (newhead.y < 0)
newhead.y = PANL_HEIGHT - CELL_WIDTH;
snake.splice(0, 0, newhead);
}
// 游戏检测
function gameMonitor() {
// 是否吃到食物
if (snake[0].x == FOOD_POINT.x && snake[0].y == FOOD_POINT.y) {
eatenFood();
}
// 是否吃到自己
for (let i = 1; i < snake.length; i++) {
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
gameOverState('you eaten yourself');
}
}
// 是否吃完了所有的食物
if (snake.length == PANL_HEIGHT / CELL_WIDTH * PANL_WIDTH / CELL_WIDTH) {
gameOverState('you win');
}
}
// 吃到了食物
function eatenFood() {
eatRood = true;
score++;
scoreBox.innerText = score;
getFood();
}
// 游戏结束
function gameOverState(gameStateReason) {
GAMEOVER.state = true;
GAMEOVER.reaon = gameStateReason;
alert(GAMEOVER.reaon);
clearInterval(raf);
}
// 清空画布
function clearCanvase() {
cgn.clearRect(0, 0, PANL_WIDTH, PANL_HEIGHT);
}
欧克