先上代码
<!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>
</head>
<style>
canvas {
border: 1px solid #000;
}
</style>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
</body>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let isEatFood = false;
draw();
function draw() {
class Rect {
constructor(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
draw() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.strokeRect(this.x, this.y, this.width, this.height);
}
}
class Snake {
constructor(length = 0) {
this.length = length;
this.head = new Rect(
canvas.width / 2,
canvas.height / 2,
40,
40,
"red"
);
this.body = [];
let x = this.head.x - 40;
let y = this.head.y;
for (let i = 0; i < this.length; i++) {
const rect = new Rect(x, y, 40, 40, "yellow");
this.body.push(rect);
x -= 40;
}
}
// 画
drawSnake = function () {
if (this.isHit(this)) {
clearInterval(timer);
const con = confirm(
`共吃了${this.body.length - this.length}个食物,要重新开始吗`
);
if (con) {
draw();
}
return;
}
// 绘制蛇头
this.head.draw();
// 绘制蛇身
for (let i = 0; i < this.body.length; i++) {
this.body[i].draw();
}
};
// 动
moveSnake() {
const rect = new Rect(
this.head.x,
this.head.y,
this.head.width,
this.head.height,
"yellow"
);
this.body.unshift(rect);
// | 整体
isEatFood = food && this.head.x === food.x && this.head.y === food.y;
if (!isEatFood) {
this.body.pop();
} else {
// 吃到了 本次不去尾
// 再画一个食物
food = randomFood(this);
food.draw();
// 重定义有没有吃到的状态
isEatFood = false;
}
switch (this.direction) {
case 0:
this.head.x -= this.head.width; // x轴新加身体起始位置
break;
case 1:
this.head.y -= this.head.height;
break;
case 2:
this.head.x += this.head.width;
break;
case 3:
this.head.y += this.head.height;
break;
}
}
// 游戏出局定义 -- 触碰边界 || 撞到自己
isHit(snake) {
const head = snake.head;
const xLimit = head.x < 0 || head.x >= canvas.width;
const yLimit = head.y < 0 || head.y >= canvas.height;
const hitSelf = snake.body.find(
({ x, y }) => head.x === x && head.y === y
);
return xLimit || yLimit || hitSelf; // 是否出局
}
}
// 随机投食
function randomFood(snake) {
let isInSnake = true;
let rect;
while (isInSnake) {
const x = Math.round((Math.random() * (canvas.width - 40)) / 40) * 40;
const y =
Math.round((Math.random() * (canvas.height - 40)) / 40) * 40;
console.log(x, y);
// 保证是40的倍数啊
rect = new Rect(x, y, 40, 40, "blue");
// 判断食物是否与蛇头蛇身重叠
if (
(snake.head.x === x && snake.head.y === y) ||
snake.body.find((item) => item.x === x && item.y === y)
) {
isInSnake = true; // 重画
continue;
} else {
isInSnake = false;
}
}
return rect;
}
document.onkeydown = function (e) {
// 键盘事件
e = e || window.event;
// 左37 上38 右39 下40
switch (e.keyCode) {
case 37:
// 三元表达式,防止右移动时按左,下面同理(贪吃蛇可不能直接掉头)
snake.direction = snake.direction === 2 ? 2 : 0;
snake.moveSnake();
break;
case 38:
snake.direction = snake.direction === 3 ? 3 : 1;
break;
case 39:
snake.direction = snake.direction === 0 ? 0 : 2;
break;
case 40:
snake.direction = snake.direction === 1 ? 1 : 3;
break;
}
};
const snake = new Snake(3);
// 默认direction为2,也就是右
snake.direction = 2;
snake.drawSnake();
var food = randomFood(snake);
food.draw();
function animate() {
// 先清空
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 移动
snake.moveSnake();
// 再画
snake.drawSnake();
food.draw();
}
var timer = setInterval(() => {
animate();
}, 100);
}
</script>
</html>
截图如下:
第零步 -- 准备工作先做好~
canvas初始化:我们的画布
<!DOCTYPE html>
<html lang="en">
<head>
<title>贪吃蛇</title>
</head>
<style>
canvas {
border: 1px solid #000;
}
</style>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
</body>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
</script>
</html>
下面,我们就开始画起来吧~ 要玩贪吃蛇,首先我们要有一条蛇
第一步 -- 画蛇
蛇是由蛇头和蛇身组成的,而蛇头和蛇身都是由小方块组成的,只是颜色有所不同,所以要画蛇,就要先画小方块
class Rect {
constructor(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
draw() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.strokeRect(this.x, this.y, this.width, this.height);
}
}
然后就可以根据起始位置,宽高和颜色来拼蛇了
<!DOCTYPE html>
<html lang="en">
<head>
<title>贪吃蛇</title>
</head>
<style>
canvas {
border: 1px solid #000;
}
</style>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
</body>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let isEatFood = false;
draw();
function draw() {
class Rect {
constructor(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
draw() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.strokeRect(this.x, this.y, this.width, this.height);
}
}
class Snake {
constructor(length = 0) {
this.length = length;
this.head = new Rect(
canvas.width / 2,
canvas.height / 2,
40,
40,
"blue"
);
this.body = [];
let x = this.head.x - 40;
let y = this.head.y;
for (let i = 0; i < this.length; i++) {
const rect = new Rect(x, y, 40, 40, "green");
this.body.push(rect);
x -= 40;
}
}
// 画
drawSnake = function () {
// 绘制蛇头
this.head.draw();
// 绘制蛇身
for (let i = 0; i < this.body.length; i++) {
this.body[i].draw();
}
};
}
const snake = new Snake(3);
snake.drawSnake();
}
</script>
</html>
这样,一只静止的小蛇就出现啦!
第二步 -- 利用定时器,让蛇动起来
让我们梳理一下规则
- 1、一次移动一个方格的距离 -- 头部加一,尾部减一来形成这样的视觉误差
- 2、蛇一开始就会默认向右移动
- 3、通过方向键盘事件,往不同方向移动
- 4、不能向反方向移动
<!DOCTYPE html>
<html lang="en">
<head>
<title>贪吃蛇</title>
</head>
<style>
canvas {
border: 1px solid #000;
}
</style>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
</body>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
draw();
function draw() {
class Rect {
constructor(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
draw() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.strokeRect(this.x, this.y, this.width, this.height);
}
}
class Snake {
constructor(length = 0) {
this.length = length;
this.head = new Rect(
canvas.width / 2,
canvas.height / 2,
40,
40,
"blue"
);
this.body = [];
let x = this.head.x - 40;
let y = this.head.y;
for (let i = 0; i < this.length; i++) {
const rect = new Rect(x, y, 40, 40, "green");
this.body.push(rect);
x -= 40;
}
}
// 画
drawSnake = function () {
// 绘制蛇头
this.head.draw();
// 绘制蛇身
for (let i = 0; i < this.body.length; i++) {
this.body[i].draw();
}
};
// 动
moveSnake() {
const rect = new Rect(
this.head.x,
this.head.y,
this.head.width,
this.head.height,
"green"
);
this.body.unshift(rect);
this.body.pop();
switch (this.direction) {
case 0:
this.head.x -= this.head.width; // x轴新加身体起始位置
break;
case 1:
this.head.y -= this.head.height;
break;
case 2:
this.head.x += this.head.width;
break;
case 3:
this.head.y += this.head.height;
break;
}
}
}
document.onkeydown = function (e) {
// 键盘事件
e = e || window.event;
// 左37 上38 右39 下40
switch (e.keyCode) {
case 37:
// 三元表达式,防止右移动时按左,下面同理(贪吃蛇可不能直接掉头)
snake.direction = snake.direction === 2 ? 2 : 0;
snake.moveSnake();
break;
case 38:
snake.direction = snake.direction === 3 ? 3 : 1;
break;
case 39:
snake.direction = snake.direction === 0 ? 0 : 2;
break;
case 40:
snake.direction = snake.direction === 1 ? 1 : 3;
break;
}
};
const snake = new Snake(3);
// 默认direction为2,也就是右
snake.direction = 2;
snake.drawSnake();
function animate() {
// 先清空
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 移动
snake.moveSnake();
// 再画
snake.drawSnake();
}
var timer = setInterval(() => {
animate();
}, 100);
}
</script>
</html>
第三步 -- 画出食物
蛇动起来是为了什么呢,当然是为了吃鸭! 所以接下来,我们就要开始游戏奖励-投食了 梳理一下投食的规则:
- 食物要在画布上
- 食物不可以和蛇重叠
- 食物的本质也是一个不同颜色的小方块 // 随机投食
function randomFood(snake) {
let isInSnake = true;
let rect;
while (isInSnake) {
const x = Math.round((Math.random() * (canvas.width - 40)) / 40) * 40;
const y =
Math.round((Math.random() * (canvas.height - 40)) / 40) * 40;
console.log(x, y);
// 保证是40的倍数啊
rect = new Rect(x, y, 40, 40, "red");
// 判断食物是否与蛇头蛇身重叠
if (
(snake.head.x === x && snake.head.y === y) ||
snake.body.find((item) => item.x === x && item.y === y)
) {
isInSnake = true; // 重画
continue;
} else {
isInSnake = false;
}
}
return rect;
}
var food = randomFood(snake);
food.draw();
function animate() {
// 先清空
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 移动
snake.moveSnake();
// 再画
snake.drawSnake();
food.draw();
}
到这里,一个贪吃蛇游戏的所有元素就全了~
明天我们开始梳理贪吃蛇的规则和补全游戏提示。