js实现贪吃蛇

文章目录

  • 实现方法_1
    • 1实现效果
    • 2 实现步骤
      • 2.1 移动场地
      • 2.2 游戏难度
      • 2.3 造蛇和食物
      • 2.4 蛇的移动
      • 2.5 产生食物的随机位置
    • 3 全部代码
  • 实现方法_2
    • 1 实现效果
    • 2实现想法
      • 2.1 蛇的存储和显示
      • 2.2 蛇的移动(`重难点`)
      • 2.3 吃食物
    • 3 完整代码

实现方法_1

1实现效果

在这里插入图片描述

2 实现步骤

html部分忽略,布局写的太辣眼了

2.1 移动场地

用的表格td,利用双层for循环加上字符串的拼接,最终利用innnerHTML将布局显示

for (let i = 0; i < height; i++) {str += '<tr>';for (let j = 0; j < width; j++) {if (i == 0) {str += '<td>' + j + '</td>';} else if (j == 0) {str += '<td>' + i + '</td>';} else {str += '<td></td>';}}str += '</tr>';}document.querySelector('table').innerHTML = str;

2.2 游戏难度

更改定时器的时间(简单)

let difficulty = document.querySelector("#difficulty");difficulty.onchange = function () {if (!isPlay) {time = difficulty.value;clearInterval(timeId);difficulty.preventDefault();}}

2.3 造蛇和食物

利用一维数组存放蛇,但是因为需要确定蛇头和蛇身的位置需要横纵坐标,所以存放的是键值对形式的数据 {key_x: x_value , key_y: y_value}; ,如何显示蛇呢,道理和js实现动漫拼图1.0版中initEvent函数哪里说的的原理一样。snake一维数组记录的蛇身位置,移动场地是有n个tds组成的,通过snake记录的横纵坐标计算出对应的tds[index]中的index值(index=行×width+列),然后将这个td更换背景,来显示蛇。同理显示食物也是如此。

// 初始化蛇function initSnake() {let snake = [];snake[0] = getRandom();let x = snake[0].x;let y = snake[0].y;snake[1] = {x: x - 1,y: y};snake[2] = {x: x - 2,y: y};return snake;}//显示画面function show(snake, food) {// 1 清除所有蛇和食物tds.forEach(function (item) {item.style.backgroundColor = '';});// 2 显示蛇snake.forEach(function (item, id) {let i = parseInt(item.x);let j = parseInt(item.y);console.log(i, j);let tdId = j * width + i;console.log(tdId);if (id == 0) {tds[tdId].style.backgroundColor = 'blue';} else {tds[tdId].style.backgroundColor = 'green';}});// 3 显示食物 let tdFoodId = food.y * width + food.x;tds[tdFoodId].style.backgroundColor = 'red';}

2.4 蛇的移动

监听键盘的按键按下事件

通过按对应的wasd上左下右,来更改d的值,表示蛇头的移动方向,调用自己写的move函数,实现对应的行向或列向的加加减减实现上下移动,在配合上定时器,便实现了蛇的移动,但这是仅限于单个块的移动(蛇头),那其他块(蛇身)怎么正确随蛇身移动呢?(需要注意越界问题)

这里方法就很巧妙了,我们存放蛇用的是一维数组,而且一维数组有两个方法
pop:移除数组的尾部元素,并返回该值。
unshift:在数组头部添加新的元素。
思路:这里我们先不去考虑越界的情况,假设蛇头的下一个位置都是合法的。
首先,通过我们的按键事件,获得我们预使蛇头向那个方向移动,然后计算出新的蛇头,利用unshift将新蛇头加入snake数组,在利用pop移除最后面的一个(前面加一个,右面移除一个,这样就实现了蛇的移动,我们可以很容易想到整体一条直线的时候确实可以,那要是拐弯呢?也是可以的)

在这里插入图片描述
蛇在吃食物时,食物的位置肯定和新蛇头是重合,吃过食物之后蛇的长度应该加一,此时呢就只需要调用unshift函数,加入新蛇头就行了,不用移除后一个,在随机一个食物位置即可。
在这里插入图片描述

 // 4.1 蛇的移动方向document.addEventListener("keydown", direction);function direction(event) {if (event.keyCode == 65 && d != "RIGHT") {d = "LEFT";} else if (event.keyCode == 87 && d != "DOWN") {d = "UP";} else if (event.keyCode == 68 && d != "LEFT") {d = "RIGHT";} else if (event.keyCode == 83 && d != "UP") {d = "DOWN";} else if (event.keyCode == 32) {// 暂停suspendGame();} else if (event.keyCode == 82) {// 重新开始restartGame();} else if (event.keyCode == 66 && !isPlay) {// 开始startGame();}}// 4.2 蛇的移动function move(snake) {let snakeX = snake[0].x;let snakeY = snake[0].y;let isEat = false;if (d == "LEFT") snakeX -= speed;if (d == "UP") snakeY -= speed;if (d == "RIGHT") snakeX += speed;if (d == "DOWN") snakeY += speed;if (snakeX == food.x && snakeY == food.y) {isEat = true;let tdId = food.y * width + food.x;tds[tdId].style.backgroundColor = 'lightgray';score += 5;scoreSpan.innerHTML = score;food = getFood(snake);}let newHead = {x: snakeX,y: snakeY};// 检测是否越界,或者自己碰到自己if (snakeX < 0 || snakeX >= width || snakeY < 0 || snakeY >= height || collision(newHead, snake)) {endGame();} else {// 没有吃到,移除最后一个,在头部加一个,蛇长不变// 吃到,if不成立,蛇长不减,蛇头加一,整体变长一if (!isEat) {snake.pop();}snake.unshift(newHead);console.log("newHead=" + newHead.x + "," + newHead.y);} 

越界问题
就是构造蛇头的时候,当前的蛇头如果已经处在上下左右某个边界了,肯定是不能在朝这个蛇头方向在移动了(这时候构造出来的新的蛇头的横纵坐标,肯定是不合法的,这里用的一维数组感觉不明显,下面的实现方法2,利用二维数组存储蛇时,这种情况,直接会索引越界报错的),而且根据游戏规则,出现越界问题时,也就是游戏结束之时(所谓的撞墙了)。所以呢,我们在产生新的蛇头坐标时,先去检查是否合法,确保不越界了,再去unshift加入数组首部,不然直接结束游戏;还有一种结束游戏,就是新的蛇头的位置,是在蛇的身体某个位置,就是自己碰自己,也直接结束游戏。

2.5 产生食物的随机位置

食物的随机位置不能在蛇身上

 // 产生随机数字(游戏开始时蛇的随机位置和食物的随机位置)function getRandom() {// 蛇头的产生范围限定在(2-44)let ran_x = parseInt(Math.random() * (width - 4)) + 2;let ran_y = parseInt(Math.random() * height);return {x: ran_x,y: ran_y};}// 检查食物随机产生的位置是否在蛇的身体上function getFood(snake) {while (true) {let food = getRandom();let flag = true;for (let i = 0; i < snake.length; i++) {let item = snake[i];if (item.x == food.x && item.y == food.y) {flag = false;break;}}if (flag) {return food;}}}

其他就是一些繁琐的获取元素,添加事件,测试逻辑,该bug了

3 全部代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>贪吃蛇</title><style>table {border-collapse: collapse;}.body {display: flex;height: 600px;background-color: lightblue;}td {border: 1px solid black;width: 15px;height: 15px;margin-left: 4px;font-size: 10px;}.print {flex: 70%;background-color: aliceblue;border: 1px solid red;width: 800px;}.content {margin: 0 auto;margin-top: 30px;width: 800px;background-color: lightgray;}.option {margin-top: 40px;flex: 20%;}.text1 {text-align: center;}button {margin-top: 10px;margin-bottom: 10px;width: 100px;height: 40px;background-color: orange;border: none;border-radius: 20px;}button:hover {background-color: yellow;}.snake,.snake1 {display: inline-block;width: 20px;height: 20px;background-color: blue;}.snake1 {background-color: green;}.food {display: inline-block;width: 20px;height: 20px;background-color: red;}.text2 {display: inline-block;width: 170px;font-size: 14px;width: 120px;}.bt {padding-left: 30px;box-sizing: border-box;}.score {text-align: center;}.score_span {display: block;margin-top: 5px;color: red;font-size: 30px;}select {margin-top: 10px;margin-bottom: 10px;width: 100px;height: 40px;background-color: lightcoral;border: none;text-align: center;border-radius: 20px;}.move span{width: 20px;height: 20px;display: inline-block;border: 1px solid #000;color: black;background-color: white;box-shadow: 2px 2px  #000;}.w{margin-bottom: 2px;margin-right: 14%;}</style>
</head><body><div class="body"><div class="print"><div class="content"><table></table></div></div><div class="option"><div class="text1">蛇:<span class="snake"></span><span class="snake1"></span><span class="snake1"></span>&nbsp;&nbsp;&nbsp;食物:<span class="food"></span></div><div class="bt"><span class="text2">按B键开始/继续:</span><button>开始游戏</button></div><div class="bt"><span class="text2">按空格键暂停:</span> <button>暂停游戏</button></div><div class="bt"><span class="text2">按R键重开:</span> <button>重新开始</button></div><div class="bt"><span class="text2">游戏难度:</span><select id="difficulty"><option value="500">简单</option><option value="300">一般</option><option value="100">困难</option><option value="50">噩梦</option></select><p style="color: red;">选择好难度后,按开始游戏(或B键)</p></div><div class="score">游戏得分<span class="score_span">0</span><div class="move">移动:<span class="w">W</span><div><span class="A">A</span><span class="S">S</span><span class="D">D</span></div></div><p style="font-size: small;">说明:在游戏过程中更换游戏难度无效。<br>若要更换难度:<br>可在开始游戏前选择好难度<br>或者暂停游戏选好难度后,再点击开始<br>或者点击重新开始,然后选择难度后,再点击开始</p></div></div></div><script>// 1 初始化场景let str = '';let width = 45;let height = 30;let isPlay = false;// 获取分数let score = 0;let scoreSpan = document.querySelector('.score_span');// 蛇的移动速度(定时器的时间)let timeId = 0;let time = 300;const speed = 1;let d = "RIGHT";for (let i = 0; i < height; i++) {str += '<tr>';for (let j = 0; j < width; j++) {if (i == 0) {str += '<td>' + j + '</td>';} else if (j == 0) {str += '<td>' + i + '</td>';} else {str += '<td></td>';}}str += '</tr>';}document.querySelector('table').innerHTML = str;let difficulty = document.querySelector("#difficulty");difficulty.onchange = function () {if (!isPlay) {time = difficulty.value;clearInterval(timeId);difficulty.preventDefault();}}// 2 初始化蛇let snake = initSnake();// 3 初始化食物,食物产生的随机位置不要在蛇身上let food = getFood(snake);// 获取按钮let bt = document.querySelectorAll('button');// 获取所有tdlet tds = document.querySelectorAll('td');bt[0].onclick = startGame;bt[1].onclick = suspendGame;bt[2].onclick = restartGame;// 初始化show(snake, food);// 4 蛇的移动// 4.1 蛇的移动方向document.addEventListener("keydown", direction);function direction(event) {if (event.keyCode == 65 && d != "RIGHT") {d = "LEFT";} else if (event.keyCode == 87 && d != "DOWN") {d = "UP";} else if (event.keyCode == 68 && d != "LEFT") {d = "RIGHT";} else if (event.keyCode == 83 && d != "UP") {d = "DOWN";} else if (event.keyCode == 32) {// 暂停suspendGame();} else if (event.keyCode == 82) {// 重新开始restartGame();} else if (event.keyCode == 66 && !isPlay) {// 开始startGame();}}// 4.2 蛇的移动function move(snake) {let snakeX = snake[0].x;let snakeY = snake[0].y;let isEat = false;if (d == "LEFT") snakeX -= speed;if (d == "UP") snakeY -= speed;if (d == "RIGHT") snakeX += speed;if (d == "DOWN") snakeY += speed;if (snakeX == food.x && snakeY == food.y) {isEat = true;let tdId = food.y * width + food.x;tds[tdId].style.backgroundColor = 'lightgray';score += 5;scoreSpan.innerHTML = score;food = getFood(snake);}let newHead = {x: snakeX,y: snakeY};// 检测是否越界,或者自己碰到自己if (snakeX < 0 || snakeX >= width || snakeY < 0 || snakeY >= height || collision(newHead, snake)) {endGame();} else {// 没有吃到,移除最后一个,在头部加一个,蛇长不变// 吃到,if不成立,蛇长不减,蛇头加一,整体变长一if (!isEat) {snake.pop();}snake.unshift(newHead);console.log("newHead=" + newHead.x + "," + newHead.y);}}//显示画面function show(snake, food) {// 1 清除所有蛇和食物tds.forEach(function (item) {item.style.backgroundColor = '';});// 2 显示蛇snake.forEach(function (item, id) {let i = parseInt(item.x);let j = parseInt(item.y);console.log(i, j);let tdId = j * width + i;console.log(tdId);if (id == 0) {tds[tdId].style.backgroundColor = 'blue';} else {tds[tdId].style.backgroundColor = 'green';}});// 3 显示食物 let tdFoodId = food.y * width + food.x;tds[tdFoodId].style.backgroundColor = 'red';}// TODO 功能按钮的实现// 开始游戏function startGame() {// clearInterval(timeId);isPlay = true;time = difficulty.value;timeId = setInterval(() => {move(snake);show(snake, food);}, time)}// 游戏结束function endGame() {clearInterval(timeId);isPlay = false;alert("游戏结束");}// 暂停游戏function suspendGame() {if (isPlay) {clearInterval(timeId);isPlay = false;alert("游戏暂停");}}// 重新开始function restartGame() {// 关闭之前的定时器clearInterval(timeId);score = 0;scoreSpan.innerHTML = score;snake = initSnake();timeId = 0;time = 300;d = 'RIGHT';isPlay = false;food = getFood(snake);show(snake, food);}// 产生随机数字(游戏开始时蛇的随机位置和食物的随机位置)function getRandom() {// 蛇头的产生范围限定在(2-44)let ran_x = parseInt(Math.random() * (width - 4)) + 2;let ran_y = parseInt(Math.random() * height);return {x: ran_x,y: ran_y};}// 检查食物随机产生的位置是否在蛇的身体上function getFood(snake) {while (true) {let food = getRandom();let flag = true;for (let i = 0; i < snake.length; i++) {let item = snake[i];if (item.x == food.x && item.y == food.y) {flag = false;break;}}if (flag) {return food;}}}// 初始化蛇function initSnake() {let snake = [];snake[0] = getRandom();let x = snake[0].x;let y = snake[0].y;snake[1] = {x: x - 1,y: y};snake[2] = {x: x - 2,y: y};return snake;}// 检查是否碰到自己function collision(head, array) {for (let i = 0; i < array.length; i++) {if (head.x == array[i].x && head.y == array[i].y) {return true;}}return false;}</script>
</body></html>

实现方法_2

完全不同的想法,很考验逻辑思维哟,做好准备

1 实现效果

没有做很好的美化,只是实现功能

在这里插入图片描述

2实现想法

2.1 蛇的存储和显示

这里蛇的存储是借助了二维数组,利用二维数组的行列情况来记录位置,达到方式一中的直接在一维数组中记录{x,y}的效果;然后在二维数组里面全部初始化为undefined(其他的<0的值也行,主要是一种初始化标记),为甚小于0呢?因为这里我们给会在后面给二维数组里面赋值(0代表食物,1到n代表蛇,1是蛇头,后面紧邻的2,3,4,…n是蛇身)

在这里插入图片描述
显示的时候,就是普通的遍历二维数组获取里面的值,做不同的处理,更换背景色。

    function show() {snake.forEach((subArr, i) => {subArr.forEach((s, j) => {let index = i * width + j;if (s == undefined) {//未定义的就是普通的tdtds[index].style.backgroundColor = '';} else if (s == 0) {//食物tds[index].style.backgroundColor = 'red';} else {if (s == 1) {//蛇头tds[index].style.backgroundColor = 'black';} else {//蛇身tds[index].style.backgroundColor = 'green';}}})})}

2.2 蛇的移动(重难点)

与上面的完全不同,上面有点取巧了,这里的考验思维了哟!

不用多说,首先肯定是去监听键盘,获取移动方向,根据不同的方向,构造出新的蛇头位置。这里构造出的新的蛇头和上面一样也要考虑越界情况(同上面)。下面我们默认新蛇头都是合法的,那么如何实现移动呢?交换

我们把新的蛇头称作目标点,把当前的蛇头称为原始点,用两个变量nextX和nextY来记录新的蛇头的横纵坐标,用x0和y0来记录当前蛇头的坐标,我们就是通过有限次数(蛇长度)的交换记录蛇的二维数组的snake[x0][y0] 和snake[nextX][nextY]里面的值,来实现蛇的移动的,当然并没这么简单(我们之前说了蛇头处存的值是1,蛇身上存的值是2-n,这还没用到的呢!)

先来个简单的示例:(右移,图中标错了)
在这里插入图片描述
通过上述我们可以知道,我们用的nextX和nextY的值和x0和y0的值,肯定是动态变化的,如何变?
显然:第一步的移动中(蛇头的移动)nextX和nextY的值记录的就是新蛇头的位置坐标,在后面的过程中,nextX和nextY的值就是前一次交换过程中原始块的值(比如,蛇头前移一下,原始蛇头的位置,就是下一个原始块的目标位【用目标块记录】)。

整体在一条线上时,我们都好理解。
那么问题来了? 要是蛇身盘踞呢?还有就是我们通过for循环控制交换实现蛇的移动,我们每次循环多少次呢?
先说简单的,交换多少次,我们定义一个变量用来记录蛇长,for循环的次数就是当前蛇的长度(蛇头蛇身都要和目标块交换)。
再来说这个最不好想的,就是蛇的转向和盘踞移动,这里就用到了我们前面给蛇身上的标号,看下图详解。

(右移,图中标错了)
在这里插入图片描述

找寻标号,我们只需要找寻上一个原始块所处位置的下左右四个方位的即可。
当然找的时候也要注意到越界的情况:

上下越界:
我们定义的snake是一维数组,在初始化时,动态的给他添加行(里面放一维的数组),在再一维里面放undefined,这样构成的二维数组,这样当我们上下找寻标号的时候,就可能出现arr[i][j]i的索引值的越界情况,这个时候找寻的arr[i]是undefined,我们在去找arr[i][j],就是去找undefined中的j位置的值,肯定是语法错误的,会报错,这时我们就需要对这种情况做处理。
左右越界:
这种情况是,我们找的i索引肯定是不会出现越界的,但是j可能会越界。但是呢,这时就算越界了,我们arr[i][j]的结果最多就是得到一个undefined,然而这并没什么大的影响,所以这里我们就不处理了。

在这里插入图片描述

// 根据方向做不同的操作let nextX, nextY;if (direction == 37) {// 左nextX = snakeHeadX;nextY = snakeHeadY - 1;} else if (direction == 38) {// 上nextX = snakeHeadX - 1;nextY = snakeHeadY;} else if (direction == 39) {// 右nextX = snakeHeadX;nextY = snakeHeadY + 1;} else if (direction == 40) {// 下nextX = snakeHeadX + 1;nextY = snakeHeadY;}// 判断游戏结束if (nextX >= height || nextX < 0 || nextY >= width || nextY < 0 || snake[nextX][nextY] > 0) {clearInterval(timeId);alert("游戏结束");return;}// 原点记录每一个蛇身小格的前一个let x0 = snakeHeadX;let y0 = snakeHeadY;// 记录新的蛇头snakeHeadX = nextX;snakeHeadY = nextY;for (let i = 1; i <= snakeLength; i++) {let temp = snake[x0][y0];snake[x0][y0] = snake[nextX][nextY];snake[nextX][nextY] = temp;// 蛇身交换后,原点记录的蛇身小格的前一个,就是蛇身下个小格要前移的目标nextX = x0;nextY = y0;// 查找上下左右四个方位,看snake二维数组中的值,是不是上面刚交换的下一个值(在蛇身上放的数值1,2,3...)// 向上找if ((nextX - 1) > -1 && snake[nextX - 1][nextY] == i + 1) {x0--;}// 向下找if ((nextX + 1) < height && snake[nextX + 1][nextY] == i + 1) {x0++;}// 向左找if (snake[nextX][nextY - 1] == i + 1) {y0--;}// 向右找if (snake[nextX][nextY + 1] == i + 1) {y0++;}}

2.3 吃食物

这里不同于上面的方法一。同样,当新蛇头的位置与食物的位置重合时(食物身上的值为0),说明吃到食物,我们将记录蛇长的变量+1,同时在完成最后一次交换后x0和y0的值,就是之前蛇的尾部的位置,我们将这个位置在二维数组中做上标号(上边提到的2-n的标号),这也就是新的蛇尾,然后在随机一个食物位置即可。

当然这里随机食物,和之前的想法一样,不能随机到蛇身上,而我们给存放蛇的二维数组做了初始化,除了蛇之外都是undefined,当随机的位置是undefined时,就说明不在蛇身上。

if (snake[x0][y0] == 0) {snake[x0][y0] = snakeLength + 1;snakeLength++;randomFood();
}

其他的就没什么了,就是绑定事件,测试调整逻辑

3 完整代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {margin: 0;padding: 0;}td {border: 1px solid #000;width: 20px;height: 20px;font-size: 13px;}.div1 {background-color: lightblue;height: 30px;padding-top: 5px;padding-left: 30px;box-sizing: border-box;}</style>
</head><body><div class="div1"><button id="start">开始游戏</button><button id="stop">暂停游戏</button>难度等级<select id="select"><option value="500">简单</option><option value="300">一般</option><option value="100">困难</option><option value="40">噩梦</option></select>场地大小<select id="size"><option value="15">15x15</option><option value="29">29x29</option><option value="35">35x29</option><option value="50">50x29</option><option value="60">60x29</option></select></div><div class="div2"><table></table></div><script>let tableContent = '';let width;let height = 29;let snake = [];let timeId = 0;let time = 500;let table = document.querySelector('table');let snakeLength;let direction;let tds;let snakeHeadX;let snakeHeadY;let isStop = false;let isPlay = false;let select = document.querySelector('#select');select.onchange = function () {time = this.value;}let sizeSelect = document.querySelector('#size');sizeSelect.onchange = function () {width = this.value;}window.onkeyup = function (e) {// 左37 上38 右39 下40if (e.keyCode >= 37 && e.keyCode <= 40) {if (((direction == 37 || direction == 39) && (e.keyCode == 38 || e.keyCode == 40)) ||((direction == 38 || direction == 40) && (e.keyCode == 37 || e.keyCode == 39))) {direction = e.keyCode;}}}let startBtn = document.querySelector('#start');startBtn.onclick = function () {clearInterval(timeId);tableContent = '';snake = [];snakeLength = 2;direction = 39;initTable();startGame();randomFood();if (!isPlay) {startBtn.innerHTML = '重新开始';} else {startBtn.innerHTML = '开始游戏';}isPlay = !isPlay;}let stopBtn = document.querySelector('#stop');stopBtn.onclick = function () {if (!isStop) {clearInterval(timeId);stopBtn.innerHTML = '继续游戏';} else {startGame();stopBtn.innerHTML = '暂停游戏';}isStop = !isStop;}function initTable() {width = sizeSelect.value;if(width==15) {height=15;}else{height=29;}time = select.value;for (let i = 0; i < height; i++) {tableContent += '<tr>';snake[i] = [];for (let j = 0; j < width; j++) {if (i == 0) {tableContent += '<td>' + j + '</td>';} else if (j == 0) {tableContent += '<td>' + i + '</td>';} else {tableContent += '<td></td>';}snake[i][j] = undefined;}tableContent += '</tr>';}table.innerHTML = tableContent;snakeHeadX = parseInt(height/2);snakeHeadY = parseInt(width/2);snake[snakeHeadX][snakeHeadY] = 1;snake[snakeHeadX][snakeHeadY - 1] = 2;tds = document.querySelectorAll('td');}function show() {snake.forEach((subArr, i) => {subArr.forEach((s, j) => {let index = i * width + j;if (s == undefined) {tds[index].style.backgroundColor = '';} else if (s == 0) {tds[index].style.backgroundColor = 'red';} else {if (s == 1) {tds[index].style.backgroundColor = 'black';} else {tds[index].style.backgroundColor = 'green';}}})})}function startGame() {timeId = setInterval(() => {// 根据方向做不同的操作let nextX, nextY;if (direction == 37) {// 左nextX = snakeHeadX;nextY = snakeHeadY - 1;} else if (direction == 38) {// 上nextX = snakeHeadX - 1;nextY = snakeHeadY;} else if (direction == 39) {// 右nextX = snakeHeadX;nextY = snakeHeadY + 1;} else if (direction == 40) {// 下nextX = snakeHeadX + 1;nextY = snakeHeadY;}// 判断游戏结束if (nextX >= height || nextX < 0 || nextY >= width || nextY < 0 || snake[nextX][nextY] > 0) {clearInterval(timeId);alert("游戏结束");return;}// 原点记录每一个蛇身小格的前一个let x0 = snakeHeadX;let y0 = snakeHeadY;// 记录新的蛇头snakeHeadX = nextX;snakeHeadY = nextY;for (let i = 1; i <= snakeLength; i++) {let temp = snake[x0][y0];snake[x0][y0] = snake[nextX][nextY];snake[nextX][nextY] = temp;// 蛇身交换后,原点记录的蛇身小格的前一个,就是蛇身下个小格要前移的目标nextX = x0;nextY = y0;// 查找上下左右四个方位,看snake二维数组中的值,是不是上面刚交换的下一个值(在蛇身上放的数值1,2,3...)// 向上找if ((nextX - 1) > -1 && snake[nextX - 1][nextY] == i + 1) {x0--;}// 向下找if ((nextX + 1) < height && snake[nextX + 1][nextY] == i + 1) {x0++;}// 向左找if (snake[nextX][nextY - 1] == i + 1) {y0--;}// 向右找if (snake[nextX][nextY + 1] == i + 1) {y0++;}}if (snake[x0][y0] == 0) {snake[x0][y0] = snakeLength + 1;snakeLength++;randomFood();}//显示蛇和食物show();}, time)}function randomFood() {while (true) {let x = Math.floor(Math.random() * height);let y = Math.floor(Math.random() * width);console.log(x, y);if (snake[x][y] == undefined) {snake[x][y] = 0;break;}}}</script>
</body></html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/656336.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java heap dump文件简单快速查看

如何 查看 堆内存的对象占用情况 1 准备工具&#xff1a; idea , 2、 步骤&#xff1a; 1、jmap 或者其他工具&#xff0c;获取到 heap 快照 2、更改该文件的后缀名为 xxxx.hprof 3、用idea open 这个文件 如图&#xff1a; 5、点击biggest tab , calculate xxxx 即可…

Flask 入门1:一个简单的 Web 程序

1. 关于 Flask Flask诞生于2010年&#xff0c; Armin Ronacher的一个愚人节玩笑。不过现在已经是一个用python语言基于Werkzeug工具箱编写的轻量级web开发框架&#xff0c;它主要面向需求简单&#xff0c;项目周期短的小应用。 Flask本身相当于一个内核&#xff0c;其他几乎所…

STM32-电动车报警器

STM32-电动车报警器 1.振动传感器点亮LED灯 需求:当振动传感器接收到振动信号时&#xff0c;使用中断方式点亮LED1 //重写中断服务函数&#xff0c;如果检测到EXTI中断请求&#xff0c;则进入此函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {//一根中断线上接有多个…

JY-7A/3DK/220静态电压继电器 无源型 板前接线

系列型号 JY-7A/1DK不带辅助电源电压继电器&#xff1b;JY-7B/1DK不带辅助电源电压继电器&#xff1b; JY-7/1DK/120不带辅助电源电压继电器&#xff1b;JY-7/1DK/120不带辅助电源电压继电器&#xff1b; JY-7A/1DKQ不带辅助电源电压继电器&#xff1b;JY-7B/1DKQ不带辅助电源…

优秀广大青年欢迎加入

在深在广 年龄在20-35左右 男生工作稳定&#xff0c;幽默&#xff0c;长相帅气&#xff0c;爱好旅行&#xff0c;健身&#xff0c;读书&#xff0c;做饭&#xff0c;摄像&#xff0c;养宠&#xff0c;人心公益等…… 女生青春美丽&#xff0c;暖心贴心&#xff0c;踏实过日子&a…

网络安全知识和华为防火墙

网络安全 网络空间安全 ---Cyberspace 2003年美国提出的网络空间概念 ---一个由信息基础设施组成的互相依赖的网络。 我国官方文件定义&#xff1a;网络空间为继海、陆、空、天以外的第五大人类互动领域。 通信保密阶段 --- 计算机安全阶段 --- 信息系统安全 --- 网络空间安…

Android 中的动态应用程序图标

Android 中的动态应用程序图标 一、需求二、解决方案三、方案实现四、结论 一、需求 您可能遇到过那些可以实现巧妙技巧的应用程序 - 更改应用程序图标&#xff08;也许是在您的生日那天&#xff09;&#xff0c;然后无缝切换回常规图标。这种功能会激起你的好奇心&#xff0c…

Shell脚本⑦awk

目录 一.awk概述 1.awk介绍 2.基本格式 3.工作原理 4.常见的内建变量 二.awk基本操作 1.打印文本内容 &#xff08;1&#xff09;打印磁盘使用情况 &#xff08;2&#xff09;打印字符串 &#xff08;3&#xff09;打印字符串确定文件有多少行 2.根据$n以及NR提取字…

实战教程:如何用Spring Boot和MySQL存储共享单车数据

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

【经典项目】Java入门,实现斗地主小游戏

一、需求分析 实现斗地主游戏可以涉及以下几个关键步骤和思路&#xff1a; 游戏规则定义&#xff1a;首先&#xff0c;你需要明确斗地主游戏的规则&#xff0c;包括牌的花色和大小、玩家数量、发牌顺序、出牌规则、胜利条件等。 牌的表示和初始化&#xff1a;定义一套扑克牌的…

第六讲_JavaScript原型

JavaScript原型 1. 原型的概念2. 原型继承2.1 原型链 3. class类的原型对象 1. 原型的概念 原型是 JavaScript 对象相互继承特性的机制。 每个函数都有一个 prototype 属性&#xff0c;这个属性指向一个对象&#xff0c;这个对象称为原型对象。每个对象都有一个 [[Prototype]…

网络原理-TCP/IP(1)

应用层 我们之前编写完了基本的java socket, 要知道,我们之前所写的所有代码都在应用层中,都是为了完成某项业务,如翻译等.关于应用层,后面会有专门的讲解,在此处先讲一下基础知识. 应用层对应着应用程序,是程序员打交道最多的一层,调用系统提供的网络api写出的代码都是应用层…

纯html+js+css个人博客

首页 <!DOCTYPE HTML> <html> <head> <title>博客</title> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <meta name"viewport" content"widthdevice-width, initial-sca…

TOP100 矩阵

1.73. 矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 提示&#xff1a; m matrix.lengthn matrix[0].length1 < m, n < 200-2^31 < matrix[i][j] < 2^31 - 1 思路&#xf…

SeaTunnel Web安装 一把成

安装相关jar包&#xff0c;以及SeaTunnel 和Web 打成的包&#xff0c;可以直接使用&#xff0c;但是需要安装MySQL客户端的分享&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1qrt1RAX38SgIpNklbQJ7pA 提取码&#xff1a;0kmf 1. 环境准备 环境名称版本系统环境C…

使用EasyPOI模板导出Execl表格(含有图片)

这是生成的文件效果 一、导入依赖 <!--easypoi--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.2.0</version></dependency><dependency><groupId>cn.aft…

零、环境搭建

之前一直玩python的&#xff0c;由于工作需要C版的opencv&#xff0c;故借此寒假闲暇时间&#xff0c;进行简单了解学习。 主要用到的IDE是Visual Studio和OpenCV 一、Visual Studio下载安装 我这里没找到之前的2017版本&#xff0c;就拿目前最新的2023社区版下载&#xff1a…

30款跨平台开发方案,总有个一个适合你!

1、Qt FluentUI CEF 可以覆盖所有需求 2、.NET Avalonia(SukiUI) CEF 可以覆盖所有需求 3、.NET DevExpress跨平台产品 CEF 可以覆盖所有需求 4、JavaFx(Swing) flatlaf WebView 可以覆盖所有需求 5、Java Qt &#xff08;qtjambi&#xff09; CEF 可以覆盖所有需…

C++对象模型和this指针,const修饰成员函数详解

目录 1.成员变量和成员函数分开存储 2.this指针 1.this指针概念 ​编辑 2.this指针用途 3.空指针访问成员函数 ​编辑 4.const修饰成员函数 mutable声明 1.成员变量和成员函数分开存储 空对象占用内存空间为1字节&#xff0c;这样是为了区分不同的空对象占内存的位置 …

【学习笔记】Vue3源码解析:第一部分-实现vue3环境搭建

课程地址&#xff1a;【已完结】全网最详细Vue3源码解析&#xff01;&#xff08;一行行带你手写Vue3源码&#xff09; 第一部分&#xff1a;实现vue3环境搭建&#xff08;对应课程的第1-3节&#xff09; VUE2与VUE3的对比&#xff1a; 也即vue2的痛点&#xff1a; 对TypeSc…