【JS基础案例】——贪吃蛇demo

做个小练习,用JS做个贪吃蛇demo~

思路:
①点击按钮开始,开始时要对UI和数据进行重置(清除上一次的游戏情况)

②蛇的位置用一个XY坐标数组表示

③蛇的身体其实就是个由多个XY坐标数组组成的大数组,蛇长大会把最新坐标不断往数组加入元素,因此蛇尾就是数组第一个元素

④爱心/星星 通过随机可以获得,设置一个定时器让爱心定时随机,其位置也是一个XY坐标数组

⑤蛇移动其实也是个定时器,移动时会把蛇尾位置去去除(蛇吃星星长度+1不用去除),也就是删除蛇身体数组第一个元素;

⑥判断蛇头和星星位置是否相等就是是否吃星星

⑦蛇触边、吃自己身体即死,也就是判断是否在格子内

⑧设置一个监听事件用来监听用户按键用以操作蛇移动方向

 

CSS
 * { box-sizing: border-box; } /* :root { height: 100%; } */ body { margin: 0; } body>div { border: 1px solid red; background-color: bisque; } .parent { margin: 300px auto; width: 305px; position: relative; } h1 { text-align: center; font-family: cursive; color: #a919a1; } .gameBox { width: 100%; height: 305px; border-collapse: collapse; margin-bottom: 10px; position: relative; background-color: white; } .gameBox>tbody>tr>td { border: 1px solid transparent; } #state, #btnStart { padding: 8px; } .control { width: 305px; position: relative; } #record { outline: none; width: 140px; position: absolute; right: 0; left: auto; font-size: 27px; font-weight: bold; text-align: center; color: crimson; } #state { user-select: none; visibility: hidden; outline: none; }

 

HTML
 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <link rel="stylesheet" href="./css/viperstyle.css"> </head> <body> <div> <div class="parent"> <h1>贪吃蛇蛇</h1> <!-- 游戏格子,使用了table表格 --> <table class="gameBox"> <!-- 格子11*11,table补全即可 --> tr*11>td*11 </table> <div class="control"> <input id="btnStart" type="button" value="开始" placeholder="" /> <span id="state">游戏状态</span> <input id="record" type="text" readonly="readonly" placeholder="计分板" value="0"> </div> </div> </div> <body> </html>

部分以table为例

 

JS
 // 游戏结束? let over = false; // 获取按钮元素对象 let btn = document.getElementById("btnStart"); // 获取单元格DOM元素对象 let gBox = document.getElementsByClassName("gameBox")[0]; // 开始按钮点击事件 btn.onclick = () => { btn.disabled = true; state.innerHTML = "游戏已开始"; state.style.backgroundColor = "green"; state.style.visibility = 'visible'; /**** 界面重置代码 ****/ // 防止下次开始游戏时遗留上次游戏的数据,先对数据进行重置 resetGames(); // 鼠标失去焦点,表格gBox获得焦点 btn.blur() gBox.focus(); // 开始~ play(); }; // 重置游戏函数,UI、Data function resetGames() { /**** 界面重置代码 ****/ _arrHeart = []; _arrViper = []; headX = 6; headY = 6; first = false; _keyCode = 0; over = false; press = true; record.value = '0'; for (let i = 0; i < rangeXY; i++) { for (let j = 0; j < rangeXY; j++) { gBox.rows[i].cells[j].style.backgroundColor = 'white'; } } } //方格大小,X、Y最大都是11 let rangeXY = 11; // 蛇初始坐标 let headX = 6, headY = 6; // 记录蛇的身体数据坐标的数组 let _arrViper = []; // 记录爱心坐标的数组 let _arrHeart = []; // 玩~ function play() { // 生成蛇XY坐标数组,初始位置6,6 let arrStart = [headX, headY]; // 生成蛇 createPoint(arrStart, 'indigo'); // 保存到蛇身体记录数组中 _arrViper.push(arrStart); // 调用随机生成爱心函数生成爱心 randomHeart(); // 蛇移动,调用点移动函数 viperMove(); } // 用来在指定范围内随机的函数,返回一个随机数 function randNum(start, end) { return Math.floor(start + Math.random() * (end - start - 10)); } // 给表格中指定坐标单元格进行颜色设置,arrXY指定必须传入一个XY坐标数组 function createPoint(arrXY, strColor) { // 因为表格其实就是数组,下标默认从0开始,所以要目标的坐标都要-1 后续代码中所有看到有要-1差不多都是这个情况 // arrXY[0] X // arrXY[1] Y let cell = gBox.rows[arrXY[0] - 1].cells[arrXY[1] - 1]; cell.style.backgroundColor = strColor; } // 生成爱心位置函数 function randomHeart() { // 定时器,每隔10秒重新生成一次,10000毫秒=10秒 let timerHeart = setInterval(() => { // 判断游戏是否game over if (over) { clearInterval(timerHeart); return; } // 每次生成前移除之前的爱心点 // (也就是当爱心记录数组不为空时) if (_arrHeart.length !== 0) { //消除前还得考虑,如果这个爱心已经被蛇吃掉了,那么就不要消除,也就是格子是印度紫色的话 removePoint(_arrHeart, 'green'); } // 将随机的坐标保存到爱心坐标数组 // 但随机之前要先判断随机的点是否是蛇的身体————思路:判断格子是否是印度紫色即可 _arrHeart = checkHeart(rangeXY, 'indigo'); // 生成爱心,爱心是绿色 createPoint(_arrHeart, "green"); }, 7000); //7000毫秒 } // 移除爱心位置的函数,传入的参数为爱心XY坐标数组,colorIF:如果格子是这个颜色的话就消掉 function removePoint(arrXY, colorIF) { let cell = gBox.rows[arrXY[0] - 1].cells[arrXY[1] - 1]; if (cell.style.backgroundColor === colorIF) { // 消除颜色,也就是设为白色即可 cell.style.backgroundColor = 'white'; } } // 给爱心的位置进行随机并检查是否在蛇的身体内 function checkHeart(arrXY, color) { let x = 0; let y = 0; let cell = null; do { x = randNum(arrXY, arrXY); y = randNum(arrXY, arrXY); cell = gBox.rows[x - 1].cells[y - 1]; } // 判断格子颜色是否为印度紫色即可 while (cell.style.backgroundColor === color) return [x, y]; } // 第一次按下按键,false 未,true 已 let first = false; // 记录按键代码的变量,37上,38左,39下,40右 let _keyCode = 0; // press时候允许用户按下按键 let press = true; // 事件监听用户按下按键 window.addEventListener("keydown", (e) => { // JS是没有枚举类型的,因此使用了switch if (btnStart.disabled) { // press:是否接收用户按下按键?true按下的按键代码会被记录下来,false:按下没卵用~ // 这个主要是为了防止界面还未处理完,用户再次按下按键然后_keyCode被改变,导致后续界面处理的时候触发了bug if (press) { switch (e.keyCode) { case 37: // 蛇不能向反方向移动 if (_keyCode !== 39) { //下 _keyCode = 37; // 上 } break; case 38: // 蛇不能向反方向移动 if (_keyCode !== 40) { //右 _keyCode = 38; // 左 } break; case 39: // 蛇不能向反方向移动 if (_keyCode !== 37) { //上 _keyCode = 39; // 下 } break; case 40: // 蛇不能向反方向移动 if (_keyCode !== 38) { //左 _keyCode = 40; // 右 } break; default: return; // ****这里按了别的按键直接就返回了,后面的就不再判断了**** } // 第一次按的时候会更改first为true;后面再按的时候,不会重复进行 first = true if (_keyCode !== 0 && !first) { first = !first; } // 当用户按下了按键,我们把接收用户按下按键设为false,也就是用户再次按下按键无效 // 只有当界面处理完成时才会将press 设为true,然后用户继续按下的按键就能被记录了 press = false; } } }); // 蛇移动函数 function viperMove() { // 只有当点击了开始,并且用户按下第一次指定的按键才生效 if (btnStart.disabled) { // 创建一个定时器,用来做蛇移动 let timerMove = setInterval(() => { switch (_keyCode) { case 37: headY -= 1; // 上 坐标x - 1 break; case 38: headX -= 1; // 左 坐标y - 1 break; case 39: headY += 1; // 下 坐标x + 1 break; case 40: headX += 1; // 右 坐标y + 1 break; default: return; } // 判断是否触边,触边直接Game Over... // 也就是会干掉定时器 //直接try{}catch{}拉闸了 try { // 发现格子是印度紫色的话,就意味着吃到自己的身体了,直接挂掉~ let cell = gBox.rows[headX - 1].cells[headY - 1]; if (cell.style.backgroundColor === 'indigo') { throw err; } //判断是否吃掉爱心,吃掉长度+1,也就是不做清除;判断方法:对比坐标,坐标是数组嘛~那也就是对比数组是否相等咯 if ([headX, headY].join() !== _arrHeart.join()) { // 先移除蛇尾,移除蛇尾前先把界面改了,再动记录蛇身体的数组,不然界面没法改 removePoint(_arrViper[0], 'indigo'); // 弹出蛇尾巴,也就是数组第一个元素 _arrViper.shift(); } else { // 吃到爱心,计分板+1 let score = parseInt(record.value); record.value = `${++score}`; } // 将移动位置记录到蛇身体数组中 createPoint([headX, headY], 'indigo'); _arrViper.push([headX, headY]); // 当界面处理完成,就允许用户继续按键了;这里把press设为true即可~ press = true; } catch { /****** 游戏结束时要做的事 ******/ // Game Over~~~提示 state.innerHTML = "游戏结束!"; state.style.backgroundColor = 'crimson'; alert('Game over~~~'); // over = true,告诉前边的生成爱心的定时器,游戏结束撩~~~ over = true; // 恢复按钮,以及一些提示 btn.disabled = false; // 定时器消除 clearInterval(timerMove); return; } }, 300); //300毫秒 } }

...其实后续还可以封装成一个类~

 


__EOF__

本文作者Myotsuki
本文链接https://www.cnblogs.com/myotsuki/p/16488334.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Myotsuki  阅读(199)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
点击右上角即可分享
微信分享提示

喜欢请打赏

扫描二维码打赏

支付宝打赏