手机开发网站建设,渠道网络推广,网站付款方式,深夜睡不着一个人看的正能量文章目录 实现方法_11实现效果2 实现步骤2.1 移动场地2.2 游戏难度2.3 造蛇和食物2.4 蛇的移动2.5 产生食物的随机位置 3 全部代码 实现方法_21 实现效果2实现想法2.1 蛇的存储和显示2.2 蛇的移动(重难点)2.3 吃食物 3 完整代码 实现方法_1
1实现效果 2 实现步骤 html部分忽略… 文章目录 实现方法_11实现效果2 实现步骤2.1 移动场地2.2 游戏难度2.3 造蛇和食物2.4 蛇的移动2.5 产生食物的随机位置 3 全部代码 实现方法_21 实现效果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-44let 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 langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title贪吃蛇/titlestyletable {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
/headbodydiv classbodydiv classprintdiv classcontenttable/table/div/divdiv classoptiondiv classtext1蛇span classsnake/spanspan classsnake1/spanspan classsnake1/spannbsp;nbsp;nbsp;食物span classfood/span/divdiv classbtspan classtext2按B键开始/继续/spanbutton开始游戏/button/divdiv classbtspan classtext2按空格键暂停/span button暂停游戏/button/divdiv classbtspan classtext2按R键重开/span button重新开始/button/divdiv classbtspan classtext2游戏难度/spanselect iddifficultyoption value500简单/optionoption value300一般/optionoption value100困难/optionoption value50噩梦/option/selectp stylecolor: red;选择好难度后按开始游戏或B键/p/divdiv classscore游戏得分span classscore_span0/spandiv classmove移动span classwW/spandivspan classAA/spanspan classSS/spanspan classDD/span/div/divp stylefont-size: small;说明在游戏过程中更换游戏难度无效。br若要更换难度br可在开始游戏前选择好难度br或者暂停游戏选好难度后再点击开始br或者点击重新开始然后选择难度后再点击开始/p/div/div/divscript// 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-44let 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,34…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 langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/titlestyle* {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
/headbodydiv classdiv1button idstart开始游戏/buttonbutton idstop暂停游戏/button难度等级select idselectoption value500简单/optionoption value300一般/optionoption value100困难/optionoption value40噩梦/option/select场地大小select idsizeoption value1515x15/optionoption value2929x29/optionoption value3535x29/optionoption value5050x29/optionoption value6060x29/option/select/divdiv classdiv2table/table/divscriptlet 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(width15) {height15;}else{height29;}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