:阿余
:2022-3-2-2
:如有错误,敬请指正。感谢!
使用HTML+CSS+JS 实现贪吃蛇游戏
第一步 、计算地图大小,网页设置
<!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>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20;
// 水平方向最大格子数
const ROWS = 20;
// 垂直方向最大格子数
const COLUMNS = 20;
// 获取地图元素
let map = document.getElementById('map');
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px';
map.style.height = COLUMNS * SIZE + 'px';
</script>
</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>
</head>
<style>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body{
width:20px;
height:20px;
background:black;
position:absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
drawSnake()
</script>
</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>
</head>
<style>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body{
width:20px;
height:20px;
background:black;
position:absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
// 定义一个数组,存放贪吃蛇的移动距离 , 0 号单元为贪吃蛇水平移动距离,1号单元为贪吃蛇垂直距离
// 水平方向:0 为不移动,1 为向右移动一个格子,-1 为向左移动一个格子
// 垂直方向:0 为不移动 1 为向下移动一个格子,-1 为向上移动一个格子
// 默认为 [1,0] 即贪吃蛇默认向在水平方向,向右移动
let direction = [1, 0]
// 贪吃蛇移动函数
function move() {
// 除贪吃蛇蛇头,每一截蛇身的位置等于前一个蛇身的位置 , 蛇身数组的0号单元就是蛇头,其余是蛇身
// 循环贪吃蛇蛇身数组
for (let i = snakeArr.length - 1; i > 0; i--) {
// 让每一截蛇身的位置等于他的前一截蛇身的位置
snakeArr[i].x = snakeArr[i - 1].x
snakeArr[i].y = snakeArr[i - 1].y
}
// 将蛇头,按照贪吃蛇的移动方向增加一个格子
snakeArr[0].x += direction[0]
snakeArr[0].y += direction[1]
}
// 定义一个运动函数
function run()
{
// 绘制贪吃蛇
drawSnake()
// 移动贪吃蛇
move()
}
// 使用定时器让贪吃蛇移动起来
setInterval(run,200)
</script>
</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>
</head>
<style>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body{
width:20px;
height:20px;
background:black;
position:absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
// 定义一个数组,存放贪吃蛇的移动距离 , 0 号单元为贪吃蛇水平移动距离,1号单元为贪吃蛇垂直距离
// 水平方向:0 为不移动,1 为向右移动一个格子,-1 为向左移动一个格子
// 垂直方向:0 为不移动 1 为向下移动一个格子,-1 为向上移动一个格子
// 默认为 [1,0] 即贪吃蛇默认向在水平方向,向右移动
let direction = [1, 0]
// 贪吃蛇移动函数
function move() {
// 除贪吃蛇蛇头,每一截蛇身的位置等于前一个蛇身的位置 , 蛇身数组的0号单元就是蛇头,其余是蛇身
// 循环贪吃蛇蛇身数组
for (let i = snakeArr.length - 1; i > 0; i--) {
// 让每一截蛇身的位置等于他的前一截蛇身的位置
snakeArr[i].x = snakeArr[i - 1].x
snakeArr[i].y = snakeArr[i - 1].y
}
// 将蛇头,按照贪吃蛇的移动方向增加一个格子
snakeArr[0].x += direction[0]
snakeArr[0].y += direction[1]
}
// 使用方向键,改变贪吃蛇的移动方向
// ←↑→↓ 的键代码分别是 37,38,39,40
// 移动方向规则,向右移动时候,无法直接向左移动,向上移动的时候,无法直接向下移动(即无法180反方向移动)
// 通过方向数组 direction 来判断贪吃蛇的移动方向
// 为窗口绑定 keydown 监听事件
document.onkeydown = function (e) {
switch (e.keyCode) {
// 左
case 37:
// 判断贪吃蛇是否没有向右移动
if (direction[0] !== 1) direction = [-1, 0]
break
// 上
case 38:
// 判断贪吃蛇是否没有向下移动
if (direction[1] !== 1) direction = [0, -1]
break
// 右
case 39:
// 判断贪吃蛇是否没有向左移动
if (direction[0] !== -1) direction = [1, 0]
break
// 下
case 40:
// 判断贪吃蛇是否没有向上移动
if (direction[1] !== -1) direction = [0, 1]
break
}
}
// 定义一个运动函数
function run()
{
// 绘制贪吃蛇
drawSnake()
// 移动贪吃蛇
move()
}
// 使用定时器让贪吃蛇移动起来
setInterval(run,200)
</script>
</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>
</head>
<style>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body {
width: 20px;
height: 20px;
background: black;
position: absolute;
}
.food {
width: 20px;
height: 20px;
background: purple;
position: absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 定义两个变量,来存放食物出现的格子位置
let foodX;
let foodY;
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
// 定义一个数组,存放贪吃蛇的移动距离 , 0 号单元为贪吃蛇水平移动距离,1号单元为贪吃蛇垂直距离
// 水平方向:0 为不移动,1 为向右移动一个格子,-1 为向左移动一个格子
// 垂直方向:0 为不移动 1 为向下移动一个格子,-1 为向上移动一个格子
// 默认为 [1,0] 即贪吃蛇默认向在水平方向,向右移动
let direction = [1, 0]
// 贪吃蛇移动函数
function move() {
// 除贪吃蛇蛇头,每一截蛇身的位置等于前一个蛇身的位置 , 蛇身数组的0号单元就是蛇头,其余是蛇身
// 循环贪吃蛇蛇身数组
for (let i = snakeArr.length - 1; i > 0; i--) {
// 让每一截蛇身的位置等于他的前一截蛇身的位置
snakeArr[i].x = snakeArr[i - 1].x
snakeArr[i].y = snakeArr[i - 1].y
}
// 将蛇头,按照贪吃蛇的移动方向增加一个格子
snakeArr[0].x += direction[0]
snakeArr[0].y += direction[1]
}
// 使用方向键,改变贪吃蛇的移动方向
// ←↑→↓ 的键代码分别是 37,38,39,40
// 移动方向规则,向右移动时候,无法直接向左移动,向上移动的时候,无法直接向下移动(即无法180反方向移动)
// 通过方向数组 direction 来判断贪吃蛇的移动方向
// 为窗口绑定 keydown 监听事件
document.onkeydown = function (e) {
switch (e.keyCode) {
// 左
case 37:
// 判断贪吃蛇是否没有向右移动
if (direction[0] !== 1) direction = [-1, 0]
break
// 上
case 38:
// 判断贪吃蛇是否没有向下移动
if (direction[1] !== 1) direction = [0, -1]
break
// 右
case 39:
// 判断贪吃蛇是否没有向左移动
if (direction[0] !== -1) direction = [1, 0]
break
// 下
case 40:
// 判断贪吃蛇是否没有向上移动
if (direction[1] !== -1) direction = [0, 1]
break
}
}
// 生成小零食的随机位置
function createFood() {
foodX = Math.ceil(Math.random() * (ROWS - 1))
foodY = Math.ceil(Math.random() * (COLUMNS - 1))
}
function drawFood() {
if (foodX === undefined) {
createFood()
}
let foodHtml = `<div class="food" style="left:${foodX * SIZE}px;top:${foodY * SIZE}px;"></div>`
map.innerHTML += foodHtml
}
// 定义一个运动函数
function run() {
// 绘制贪吃蛇
drawSnake()
drawFood()
// 移动贪吃蛇
move()
}
// 使用定时器让贪吃蛇移动起来
setInterval(run, 200)
</script>
</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>
</head>
<style>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body {
width: 20px;
height: 20px;
background: black;
position: absolute;
z-index: 1;
}
.food {
width: 20px;
height: 20px;
background: purple;
position: absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 定义两个变量,来存放食物出现的格子位置
let foodX;
let foodY;
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
// 定义一个数组,存放贪吃蛇的移动距离 , 0 号单元为贪吃蛇水平移动距离,1号单元为贪吃蛇垂直距离
// 水平方向:0 为不移动,1 为向右移动一个格子,-1 为向左移动一个格子
// 垂直方向:0 为不移动 1 为向下移动一个格子,-1 为向上移动一个格子
// 默认为 [1,0] 即贪吃蛇默认向在水平方向,向右移动
let direction = [1, 0]
// 贪吃蛇移动函数
function move() {
// 除贪吃蛇蛇头,每一截蛇身的位置等于前一个蛇身的位置 , 蛇身数组的0号单元就是蛇头,其余是蛇身
// 循环贪吃蛇蛇身数组
for (let i = snakeArr.length - 1; i > 0; i--) {
// 让每一截蛇身的位置等于他的前一截蛇身的位置
snakeArr[i].x = snakeArr[i - 1].x
snakeArr[i].y = snakeArr[i - 1].y
}
// 将蛇头,按照贪吃蛇的移动方向增加一个格子
snakeArr[0].x += direction[0]
snakeArr[0].y += direction[1]
}
// 使用方向键,改变贪吃蛇的移动方向
// ←↑→↓ 的键代码分别是 37,38,39,40
// 移动方向规则,向右移动时候,无法直接向左移动,向上移动的时候,无法直接向下移动(即无法180反方向移动)
// 通过方向数组 direction 来判断贪吃蛇的移动方向
// 为窗口绑定 keydown 监听事件
document.onkeydown = function (e) {
switch (e.keyCode) {
// 左
case 37:
// 判断贪吃蛇是否没有向右移动
if (direction[0] !== 1) direction = [-1, 0]
break
// 上
case 38:
// 判断贪吃蛇是否没有向下移动
if (direction[1] !== 1) direction = [0, -1]
break
// 右
case 39:
// 判断贪吃蛇是否没有向左移动
if (direction[0] !== -1) direction = [1, 0]
break
// 下
case 40:
// 判断贪吃蛇是否没有向上移动
if (direction[1] !== -1) direction = [0, 1]
break
}
}
// 生成小零食的随机位置
function createFood() {
foodX = Math.ceil(Math.random() * (ROWS - 1))
foodY = Math.ceil(Math.random() * (COLUMNS - 1))
}
// 渲染食物函数
function drawFood() {
if (foodX === undefined) {
createFood()
}
let foodHtml = `<div class="food" style="left:${foodX * SIZE}px;top:${foodY * SIZE}px;"></div>`
map.innerHTML += foodHtml
}
// 判断贪吃蛇是否吃到了小零食
function eatFood() {
// 获得贪吃蛇的蛇头
let snakeHeader = snakeArr[0];
// 如果小零食的坐标和蛇头的坐标完全一致
// 说明吃到了这个小零食
if (snakeHeader.x === foodX && snakeHeader.y === foodY) {
return true
}
return false
}
// 定义一个运动函数
function run() {
// 绘制贪吃蛇
drawSnake()
drawFood()
// 如果贪吃蛇吃到了小零食,重新生成小零食的坐标
if (eatFood()) {
createFood()
}
// 移动贪吃蛇
move()
}
// 使用定时器让贪吃蛇移动起来
setInterval(run, 200)
</script>
</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>
</head>
<style>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body {
width: 20px;
height: 20px;
background: black;
position: absolute;
z-index: 1;
}
.food {
width: 20px;
height: 20px;
background: purple;
position: absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 定义两个变量,来存放食物出现的格子位置
let foodX;
let foodY;
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
// 定义一个数组,存放贪吃蛇的移动距离 , 0 号单元为贪吃蛇水平移动距离,1号单元为贪吃蛇垂直距离
// 水平方向:0 为不移动,1 为向右移动一个格子,-1 为向左移动一个格子
// 垂直方向:0 为不移动 1 为向下移动一个格子,-1 为向上移动一个格子
// 默认为 [1,0] 即贪吃蛇默认向在水平方向,向右移动
let direction = [1, 0]
// 贪吃蛇移动函数
function move() {
// 除贪吃蛇蛇头,每一截蛇身的位置等于前一个蛇身的位置 , 蛇身数组的0号单元就是蛇头,其余是蛇身
// 循环贪吃蛇蛇身数组
for (let i = snakeArr.length - 1; i > 0; i--) {
// 让每一截蛇身的位置等于他的前一截蛇身的位置
snakeArr[i].x = snakeArr[i - 1].x
snakeArr[i].y = snakeArr[i - 1].y
}
// 将蛇头,按照贪吃蛇的移动方向增加一个格子
snakeArr[0].x += direction[0]
snakeArr[0].y += direction[1]
}
// 使用方向键,改变贪吃蛇的移动方向
// ←↑→↓ 的键代码分别是 37,38,39,40
// 移动方向规则,向右移动时候,无法直接向左移动,向上移动的时候,无法直接向下移动(即无法180反方向移动)
// 通过方向数组 direction 来判断贪吃蛇的移动方向
// 为窗口绑定 keydown 监听事件
document.onkeydown = function (e) {
switch (e.keyCode) {
// 左
case 37:
// 判断贪吃蛇是否没有向右移动
if (direction[0] !== 1) direction = [-1, 0]
break
// 上
case 38:
// 判断贪吃蛇是否没有向下移动
if (direction[1] !== 1) direction = [0, -1]
break
// 右
case 39:
// 判断贪吃蛇是否没有向左移动
if (direction[0] !== -1) direction = [1, 0]
break
// 下
case 40:
// 判断贪吃蛇是否没有向上移动
if (direction[1] !== -1) direction = [0, 1]
break
}
}
// 为贪吃蛇增加一截身体
function addBody() {
// 获取到最后一截身体
let lastBody = snakeArr[snakeArr.length - 1]
// 创建一个新的身体对象
let newBody = {
x: lastBody.x,
y: lastBody.y
}
// 将新的身体对象,添加到贪吃蛇数组中
snakeArr.push(newBody)
}
// 生成小零食的随机位置
function createFood() {
foodX = Math.ceil(Math.random() * (ROWS - 1))
foodY = Math.ceil(Math.random() * (COLUMNS - 1))
}
// 渲染食物函数
function drawFood() {
if (foodX === undefined) {
createFood()
}
let foodHtml = `<div class="food" style="left:${foodX * SIZE}px;top:${foodY * SIZE}px;"></div>`
map.innerHTML += foodHtml
}
// 判断贪吃蛇是否吃到了小零食
function eatFood() {
// 获得贪吃蛇的蛇头
let snakeHeader = snakeArr[0];
// 如果小零食的坐标和蛇头的坐标完全一致
// 说明吃到了这个小零食
if (snakeHeader.x === foodX && snakeHeader.y === foodY) {
return true
}
return false
}
// 定义一个运动函数
function run() {
// 绘制贪吃蛇
drawSnake()
drawFood()
// 如果贪吃蛇吃到了小零食,重新生成小零食的坐标
if (eatFood()) {
createFood()
}
// 移动贪吃蛇
move()
}
// 使用定时器让贪吃蛇移动起来
setInterval(run, 200)
</script>
</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>
</head>
<style>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body {
width: 20px;
height: 20px;
background: black;
position: absolute;
z-index: 1;
}
.food {
width: 20px;
height: 20px;
background: purple;
position: absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 定义两个变量,来存放食物出现的格子位置
let foodX;
let foodY;
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
// 定义一个数组,存放贪吃蛇的移动距离 , 0 号单元为贪吃蛇水平移动距离,1号单元为贪吃蛇垂直距离
// 水平方向:0 为不移动,1 为向右移动一个格子,-1 为向左移动一个格子
// 垂直方向:0 为不移动 1 为向下移动一个格子,-1 为向上移动一个格子
// 默认为 [1,0] 即贪吃蛇默认向在水平方向,向右移动
let direction = [1, 0]
// 定义一个变量, 用来存放定时器的id
let clockId;
// 贪吃蛇移动函数
function move() {
// 除贪吃蛇蛇头,每一截蛇身的位置等于前一个蛇身的位置 , 蛇身数组的0号单元就是蛇头,其余是蛇身
// 循环贪吃蛇蛇身数组
for (let i = snakeArr.length - 1; i > 0; i--) {
// 让每一截蛇身的位置等于他的前一截蛇身的位置
snakeArr[i].x = snakeArr[i - 1].x
snakeArr[i].y = snakeArr[i - 1].y
}
// 将蛇头,按照贪吃蛇的移动方向增加一个格子
snakeArr[0].x += direction[0]
snakeArr[0].y += direction[1]
}
// 使用方向键,改变贪吃蛇的移动方向
// ←↑→↓ 的键代码分别是 37,38,39,40
// 移动方向规则,向右移动时候,无法直接向左移动,向上移动的时候,无法直接向下移动(即无法180反方向移动)
// 通过方向数组 direction 来判断贪吃蛇的移动方向
// 为窗口绑定 keydown 监听事件
document.onkeydown = function (e) {
switch (e.keyCode) {
// 左
case 37:
// 判断贪吃蛇是否没有向右移动
if (direction[0] !== 1) direction = [-1, 0]
break
// 上
case 38:
// 判断贪吃蛇是否没有向下移动
if (direction[1] !== 1) direction = [0, -1]
break
// 右
case 39:
// 判断贪吃蛇是否没有向左移动
if (direction[0] !== -1) direction = [1, 0]
break
// 下
case 40:
// 判断贪吃蛇是否没有向上移动
if (direction[1] !== -1) direction = [0, 1]
break
}
}
// 为贪吃蛇增加一截身体
function addBody() {
// 获取到最后一截身体
let lastBody = snakeArr[snakeArr.length - 1]
// 创建一个新的身体对象
let newBody = {
x: lastBody.x,
y: lastBody.y
}
// 将新的身体对象,添加到贪吃蛇数组中
snakeArr.push(newBody)
}
// 生成小零食的随机位置
function createFood() {
foodX = Math.ceil(Math.random() * (ROWS - 1))
foodY = Math.ceil(Math.random() * (COLUMNS - 1))
}
// 渲染食物函数
function drawFood() {
if (foodX === undefined) {
createFood()
}
let foodHtml = `<div class="food" style="left:${foodX * SIZE}px;top:${foodY * SIZE}px;"></div>`
map.innerHTML += foodHtml
}
// 判断贪吃蛇是否吃到了小零食
function eatFood() {
// 获得贪吃蛇的蛇头
let snakeHeader = snakeArr[0];
// 如果小零食的坐标和蛇头的坐标完全一致
// 说明吃到了这个小零食
if (snakeHeader.x === foodX && snakeHeader.y === foodY) {
return true
}
return false
}
// 定义一个检测是否撞墙的函数
function checkWall() {
// 获取蛇头
let snakeHeader = snakeArr[0]
// 判断蛇头是否撞墙
if (snakeHeader.x < 0 || snakeHeader.x >= ROWS || snakeHeader.y < 0 || snakeHeader.y >= COLUMNS) {
// 提醒用户,游戏结束
alert('游戏结束了')
// 如果撞墙清除定时器
clearInterval(clockId)
}
}
// 定义一个运动函数
function run() {
// 绘制贪吃蛇
drawSnake()
drawFood()
// 如果贪吃蛇吃到了小零食,
if (eatFood()) {
// 增加一截身体
addBody()
// 重新生成小零食的坐标
createFood()
}
// 移动贪吃蛇
move()
// 判断蛇头是否撞墙
checkWall()
}
// 使用定时器让贪吃蛇移动起来
clockId = setInterval(run, 200)
</script>
</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>
</head>
<style>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body {
width: 20px;
height: 20px;
background: black;
position: absolute;
z-index: 1;
}
.food {
width: 20px;
height: 20px;
background: purple;
position: absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 定义两个变量,来存放食物出现的格子位置
let foodX;
let foodY;
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
// 定义一个数组,存放贪吃蛇的移动距离 , 0 号单元为贪吃蛇水平移动距离,1号单元为贪吃蛇垂直距离
// 水平方向:0 为不移动,1 为向右移动一个格子,-1 为向左移动一个格子
// 垂直方向:0 为不移动 1 为向下移动一个格子,-1 为向上移动一个格子
// 默认为 [1,0] 即贪吃蛇默认向在水平方向,向右移动
let direction = [1, 0]
// 定义一个变量, 用来存放定时器的id
let clockId;
// 贪吃蛇移动函数
function move() {
// 除贪吃蛇蛇头,每一截蛇身的位置等于前一个蛇身的位置 , 蛇身数组的0号单元就是蛇头,其余是蛇身
// 循环贪吃蛇蛇身数组
for (let i = snakeArr.length - 1; i > 0; i--) {
// 让每一截蛇身的位置等于他的前一截蛇身的位置
snakeArr[i].x = snakeArr[i - 1].x
snakeArr[i].y = snakeArr[i - 1].y
}
// 将蛇头,按照贪吃蛇的移动方向增加一个格子
snakeArr[0].x += direction[0]
snakeArr[0].y += direction[1]
}
// 使用方向键,改变贪吃蛇的移动方向
// ←↑→↓ 的键代码分别是 37,38,39,40
// 移动方向规则,向右移动时候,无法直接向左移动,向上移动的时候,无法直接向下移动(即无法180反方向移动)
// 通过方向数组 direction 来判断贪吃蛇的移动方向
// 为窗口绑定 keydown 监听事件
document.onkeydown = function (e) {
switch (e.keyCode) {
// 左
case 37:
// 判断贪吃蛇是否没有向右移动
if (direction[0] !== 1) direction = [-1, 0]
break
// 上
case 38:
// 判断贪吃蛇是否没有向下移动
if (direction[1] !== 1) direction = [0, -1]
break
// 右
case 39:
// 判断贪吃蛇是否没有向左移动
if (direction[0] !== -1) direction = [1, 0]
break
// 下
case 40:
// 判断贪吃蛇是否没有向上移动
if (direction[1] !== -1) direction = [0, 1]
break
}
}
// 为贪吃蛇增加一截身体
function addBody() {
// 获取到最后一截身体
let lastBody = snakeArr[snakeArr.length - 1]
// 创建一个新的身体对象
let newBody = {
x: lastBody.x,
y: lastBody.y
}
// 将新的身体对象,添加到贪吃蛇数组中
snakeArr.push(newBody)
}
// 生成小零食的随机位置
function createFood() {
foodX = Math.ceil(Math.random() * (ROWS - 1))
foodY = Math.ceil(Math.random() * (COLUMNS - 1))
}
// 渲染食物函数
function drawFood() {
if (foodX === undefined) {
createFood()
}
let foodHtml = `<div class="food" style="left:${foodX * SIZE}px;top:${foodY * SIZE}px;"></div>`
map.innerHTML += foodHtml
}
// 判断贪吃蛇是否吃到了小零食
function eatFood() {
// 获得贪吃蛇的蛇头
let snakeHeader = snakeArr[0];
// 如果小零食的坐标和蛇头的坐标完全一致
// 说明吃到了这个小零食
if (snakeHeader.x === foodX && snakeHeader.y === foodY) {
return true
}
return false
}
// 定义一个检测是否撞墙的函数
function checkWall() {
// 获取蛇头
let snakeHeader = snakeArr[0]
// 判断蛇头是否撞墙
if (snakeHeader.x < 0 || snakeHeader.x >= ROWS || snakeHeader.y < 0 || snakeHeader.y >= COLUMNS) {
// 提醒用户,游戏结束
alert('游戏结束了')
// 如果撞墙清除定时器
clearInterval(clockId)
}
}
// 定义一个是否吃到自己的函数
function checkSelf() {
// 获取蛇头
let snakeHeader = snakeArr[0]
// 遍历蛇身
for (let i = 1; i < snakeArr.length; i++) {
// 判断蛇头是否和蛇身重合
if (snakeHeader.x === snakeArr[i].x && snakeHeader.y === snakeArr[i].y) {
alert('游戏结束了')
clearInterval(clockId)
}
}
}
// 定义一个运动函数
function run() {
// 绘制贪吃蛇
drawSnake()
drawFood()
// 如果贪吃蛇吃到了小零食,
if (eatFood()) {
// 增加一截身体
addBody()
// 重新生成小零食的坐标
createFood()
}
// 移动贪吃蛇
move()
// 判断蛇头是否撞墙
checkWall()
// 判断蛇是否咬到了自己
checkSelf()
}
// 使用定时器让贪吃蛇移动起来
clockId = setInterval(run, 200)
</script>
</html>
第十步、优化可以180°改变移动方向的bug
如果贪吃蛇现在是向右移动,我们快速按下向邻的方向键(左键或者上键),然后按下左键就可以实现 180 掉头
这是因为,我们按下相邻方向键的时候,已经改变的贪食蛇的移动方向数组,但是贪吃蛇此刻还没有移动,然后再按下反方向键就可以逃过程序判断
解决方法:定义一个变量来记录转向移动的状态,如果按了方向键,就锁定改变方向的状态,等贪吃蛇成功移动后,再解锁
<!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>
#map {
border: 2px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.snake-body {
width: 20px;
height: 20px;
background: black;
position: absolute;
z-index: 1;
}
.snake-body:first-child{
border-radius:50%;
background-color:pink;
z-index:2;
}
.food {
width: 20px;
height: 20px;
background: purple;
position: absolute;
}
</style>
<body>
<div id="map">
</div>
</body>
<script>
// 定义地图的相关数据
// 设定一个小单元格子的大小为 20px,20px 的正方形
const SIZE = 20
// 水平方向最大格子数
const ROWS = 20
// 垂直方向最大格子数
const COLUMNS = 20
// 获取地图元素
let map = document.getElementById('map')
// 计算出并设置地图宽度和高度
map.style.width = ROWS * SIZE + 'px'
map.style.height = COLUMNS * SIZE + 'px'
// 使用数组来定义贪吃蛇的身体,默认只有两截身体
// 每个数组单元代表一截贪吃蛇的身体
// x 代表这截身体在水平方向第 x 个格子上
// y 代表这截身体在垂直方向第 y 个格子上
let snakeArr = [
{
x: 5,
y: 1
},
{
x: 4,
y: 1
}
]
// 定义两个变量,来存放食物出现的格子位置
let foodX;
let foodY;
// 贪吃蛇绘制函数
function drawSnake() {
// 拼凑蛇身的 html 字符串
let snakeHtml = ''
// 循环所有的蛇身,并计算出该蛇身所处的 left 和 top 偏移 (格子数 * 格子大小 + px)
for (let i = 0; i < snakeArr.length; i++) {
snakeHtml += `<div class="snake-body" style="left:${snakeArr[i].x * SIZE}px;top:${snakeArr[i].y * SIZE}px;"></div>`
}
// 将拼凑好的蛇身html 字符串,设置为 map 的 innerHTML,重新显示
map.innerHTML = snakeHtml
}
// 定义一个数组,存放贪吃蛇的移动距离 , 0 号单元为贪吃蛇水平移动距离,1号单元为贪吃蛇垂直距离
// 水平方向:0 为不移动,1 为向右移动一个格子,-1 为向左移动一个格子
// 垂直方向:0 为不移动 1 为向下移动一个格子,-1 为向上移动一个格子
// 默认为 [1,0] 即贪吃蛇默认向在水平方向,向右移动
let direction = [1, 0]
// 定义一个变量, 用来存放定时器的id
let clockId;
// 定义一个变量,用来记录是否等待转向
let isTurn = false;
// 贪吃蛇移动函数
function move() {
// 除贪吃蛇蛇头,每一截蛇身的位置等于前一个蛇身的位置 , 蛇身数组的0号单元就是蛇头,其余是蛇身
// 循环贪吃蛇蛇身数组
for (let i = snakeArr.length - 1; i > 0; i--) {
// 让每一截蛇身的位置等于他的前一截蛇身的位置
snakeArr[i].x = snakeArr[i - 1].x
snakeArr[i].y = snakeArr[i - 1].y
}
// 将蛇头,按照贪吃蛇的移动方向增加一个格子
snakeArr[0].x += direction[0]
snakeArr[0].y += direction[1]
isTurn = false
}
// 使用方向键,改变贪吃蛇的移动方向
// ←↑→↓ 的键代码分别是 37,38,39,40
// 移动方向规则,向右移动时候,无法直接向左移动,向上移动的时候,无法直接向下移动(即无法180反方向移动)
// 通过方向数组 direction 来判断贪吃蛇的移动方向
// 为窗口绑定 keydown 监听事件
document.onkeydown = function (e) {
if (isTurn) {
return
}
switch (e.keyCode) {
// 左
case 37:
// 判断贪吃蛇是否没有向右移动
if (direction[0] !== 1) {
isTurn = true
direction = [-1, 0]
}
break
// 上
case 38:
// 判断贪吃蛇是否没有向下移动
if (direction[1] !== 1) {
isTurn = true
direction = [0, -1]
}
break
// 右
case 39:
// 判断贪吃蛇是否没有向左移动
if (direction[0] !== -1) {
isTurn = true
direction = [1, 0]
}
break
// 下
case 40:
// 判断贪吃蛇是否没有向上移动
if (direction[1] !== -1) {
isTurn = true
direction = [0, 1]
}
break
}
}
// 为贪吃蛇增加一截身体
function addBody() {
// 获取到最后一截身体
let lastBody = snakeArr[snakeArr.length - 1]
// 创建一个新的身体对象
let newBody = {
x: lastBody.x,
y: lastBody.y
}
// 将新的身体对象,添加到贪吃蛇数组中
snakeArr.push(newBody)
}
// 生成小零食的随机位置
function createFood() {
foodX = Math.ceil(Math.random() * (ROWS - 1))
foodY = Math.ceil(Math.random() * (COLUMNS - 1))
}
// 渲染食物函数
function drawFood() {
if (foodX === undefined) {
createFood()
}
let foodHtml = `<div class="food" style="left:${foodX * SIZE}px;top:${foodY * SIZE}px;"></div>`
map.innerHTML += foodHtml
}
// 判断贪吃蛇是否吃到了小零食
function eatFood() {
// 获得贪吃蛇的蛇头
let snakeHeader = snakeArr[0];
// 如果小零食的坐标和蛇头的坐标完全一致
// 说明吃到了这个小零食
if (snakeHeader.x === foodX && snakeHeader.y === foodY) {
return true
}
return false
}
// 定义一个检测是否撞墙的函数
function checkWall() {
// 获取蛇头
let snakeHeader = snakeArr[0]
// 判断蛇头是否撞墙
if (snakeHeader.x < 0 || snakeHeader.x >= ROWS || snakeHeader.y < 0 || snakeHeader.y >= COLUMNS) {
// 提醒用户,游戏结束
alert('游戏结束了')
// 如果撞墙清除定时器
clearInterval(clockId)
}
}
// 定义一个是否吃到自己的函数
function checkSelf() {
// 获取蛇头
let snakeHeader = snakeArr[0]
// 遍历蛇身
for (let i = 1; i < snakeArr.length; i++) {
// 判断蛇头是否和蛇身重合
if (snakeHeader.x === snakeArr[i].x && snakeHeader.y === snakeArr[i].y) {
alert('游戏结束了')
clearInterval(clockId)
}
}
}
// 定义一个运动函数
function run() {
// 绘制贪吃蛇
drawSnake()
drawFood()
// 如果贪吃蛇吃到了小零食,
if (eatFood()) {
// 增加一截身体
addBody()
// 重新生成小零食的坐标
createFood()
}
// 移动贪吃蛇
move()
// 判断蛇头是否撞墙
checkWall()
// 判断蛇是否咬到了自己
checkSelf()
}
// 使用定时器让贪吃蛇移动起来
clockId = setInterval(run, 200)
</script>
</html>