当前位置:   article > 正文

Js贪吃蛇开发过程详细

js 贪吃蛇开发

最近看百度知道时有人提问js实现贪吃蛇,突然来了兴致想自己写一个。从来没写过贪吃蛇,也不知道应该怎么实现,后来自己琢磨了一个方法,不知道会不会有更好的,直接贴代码的话怕有人看不懂,于是想把整个开发过程详细写出来和大家交流学习,如下:

开发需求:

需求大家玩过贪吃蛇应该都了解,用上下左右控制蛇的方向移动,吃到闪动的小虫就增长一段并加分。直到撞到四壁或吃到自己游戏结束。

设计思路:

首先要有一个地图供蛇爬行。由于蛇的形状长度均会改变,如果用图片表示的话肯定不行,其实蛇的移动可以看做是背景色的黑白变化,蛇头的前一个html元素变黑,蛇为变白,则可看成是向前移动了一步。你整个地图的移动则需将整个地图分为多个小的html元素,这里我们选择table做地图框架,里面包含的td做蛇的位移就可以了。我们可以做一个30*30的表格做地图。然后通过改变900个td的背景色的变化来控制蛇的位移。

开发详细:

初始构思是这样画出方格阵做地图,然后3个方格是黑色背景表示的蛇。第四个变黑,第一个变白则可看做是向前移动了一步。怎样控制前后变色呢?开始我想到的是用js获取当前元素的前后兄弟元素的方法控制首尾的td背景颜色改变,但这种方法后来失败,只能适用于直线前行的移动,如果向下掉头的户js获取下方的td就难了。其实等把整个地图画出来就想到了,应该用二维数组。二维数组的一维表示tr行,二维表示每行的td,这样把地图的每个元素都存入二维数组,可以按a[x][y]的x,y算法很快的调用到要移动的位置。

其它的蛇怎么动怎么吃虫子先不考虑,先做出这个table和数组。

css

  1. #she{border-collapse:collapse;}
  2. #she td{
  3. width:15px;
  4. height:15px;
  5. border:1px solid grey;
  6. }
  7. .hei{ background:#000;}/*黑色背景*/
  8. .bai{ background:#ffff;}/*白色背景*/

body页面为

<table cellpadding="0" cellspacing="0" id="she" align="center"></table>

js

  1. var allTds = new Array();//用于保存所有的td的二维数组
  2. var ck = 30;//地图长宽
  3. window.οnlοad=function(){//网页加载时执行
  4. for(var i = 0;i<ck;i++){//控制ck个行即tr
  5. var tr = document.createElement("tr");
  6. var trs = new Array();//声明一个数组用于保存此行的ck个td.
  7. for(var j=0;j<ck;j++){//控制每个行里的元素即tr里的ck个td,如果不想画正方形ck可分开写两个参数,一个高一个宽
  8. var td=document.createElement("td");
  9. trs[j] = td;
  10. tr.appendChild(td);
  11. }
  12. document.getElementById("she").appendChild(tr);
  13. allTds[i] = trs;//保存到二维数组
  14. }

这样就画出了一个ck*ck的表格,此处用的30,即30*30;生成表格的同时生成了数组。

然后我们初始化蛇的位置在前k个,k为蛇的初始长度可自行设定。这个没啥难的,循环allTds[0][0]到allTds[0][k-1]将其className设为css的“hei”即可。

然后的要点是我们要考虑这条蛇应该怎么移动,实质是蛇头的相邻td和蛇尾的td的背景变化。蛇头好说如果当前蛇头是allTds[x][y],则移动一步后向前则变为allTds[x][y+1],向下舌头变为allTds[x+1][y]。但如何知道当前哪个是蛇头?经过很多次走动拐弯后如何确定,还有蛇尾,蛇尾的td变白色后,哪个td是下一个蛇尾,是左侧还是右侧还是上下都有可能。

这时我想到一个办法是用一个一维数组var she = new Array();表示蛇身,将表示蛇神的td元素依次从尾到头存入she[0]....she[n].这样移动一步后新蛇头可以根据移动方向确定下一个蛇头是谁,将she[n]替换为新的蛇头,前面的she[n-]的值替换为she[n]....一次替换到she[0]是蛇尾变为了she[1].she[0]原有值被挤掉。这样就移动了一步!每移动一步前先这个数组中所有元素变白,即蛇消除掉,变化数组后此数组的所有元素再变黑,蛇再显示出来,就移动了,这样蛇头蛇尾的变化都解决 ,不用考虑蛇身。

思路有了接下来就做。

首先蛇头是根据方向来移动的,需要一个控制方向的函数,蛇的消除和显示可以单独做两个个带参函数供调用如下

  1. var dir = 1;//方向,1前,2后,3上,4下
  2. function keyDown(event){//接受键盘改变方向
  3. if(event.keyCode==39){//左箭头
  4. dir =1;
  5. }
  6. if(event.keyCode==37){//右箭头
  7. dir =2;
  8. }
  9. if(event.keyCode==38){//上箭头
  10. dir =3;
  11. }
  12. if(event.keyCode==40){//下箭头
  13. dir =4;
  14. }
  15. }
  1. function sheYin(obj){//蛇隐 其实不用全部消除,只消除蛇身数组的第一个,因为我们是按蛇尾到蛇头排的数组
  2. obj[0].className = "bai";
  3. }
  4. function sheXian(obj){//蛇现同理
  5. obj[obj.length-1].className = "hei";
  6. }

开始的初始化改为:

  1. var allTds = new Array();//用于保存所有的td的二维数组
  2. var ck = 30;//地图长宽
  3. var k=3;//初始长度
  4. var she = new Array();//初始化蛇
  5. var x = 0;y = k-1;//蛇头坐标
  6. var dir = 1;//方向,1前,2后,3上,4下
  7. window.οnlοad=function(){//网页加载时执行
  8. for(var i = 0;i<ck;i++){//控制ck个行即tr
  9. var tr = document.getElementById("she").insertRow(-1);
  10. var trs = new Array();//声明一个数组用于保存此行的ck个td.
  11. for(var j=0;j<ck;j++){//控制每个行里的元素即tr里的ck个td,如果不想画正方形ck可分开写两个参数,一个高一个宽
  12. var td=document.createElement("td");
  13. trs[j] = td;
  14. tr.appendChild(td);
  15. }
  16. allTds[i] = trs;//保存到二维数组
  17. }
  18. for(var g = 0;g< k;g++){//初始化蛇身数组
  19. she[g] = allTds[0][g];
  20. allTds[0][g].className = "hei";
  21. }
  22. }

 

  1. function weStart(){
  2. sheYin(she);//先消除蛇尾
  3. for(var o=0;o<she.length-1;o++){//将蛇身数组依次前移,挤掉蛇尾
  4. she[o] = she[o+1];
  5. }
  6. if(dir==1){//如果是横向前行
  7. she[k-1] = allTds[x][y+1];//任命新蛇头
  8. y+=1;//改变蛇头坐标
  9. }
  10. if(dir==2){//同理
  11. she[k-1] = allTds[x][y-1];
  12. y-=1;//改变蛇头坐标
  13. }
  14. if(dir==3){//同理
  15. she[k-1] = allTds[x-1][y];
  16. x-=1;//改变蛇头坐标
  17. }
  18. if(dir==4){//同理
  19. she[k-1] = allTds[x+1][y];
  20. x+=1;//改变蛇头坐标
  21. }
  22. sheXian(she);//将新蛇头点黑
  23. setTimeout("weStart()",500);//隔半秒后再执行一次,就是半秒走一步的意思
  24. }

原来的地图table下再画个table放开始结束等按钮:

  1. <table cellpadding="0" cellspacing="0" id="she" align="center"></table>
  2. <table cellpadding="0" cellspacing="0" width="300px;" align="center">
  3. <tr>
  4. <td><input type="button" value="Start" οnclick="weStart()"/></td>
  5. <td>得分:<input id="mark" type="text" value="0" readonly size=3/></td>
  6. <td><input type="button" value="Stop" οnclick="weStop()"/></td>
  7. </tr>
  8. </table>

这时已经是一个粗糙的可以通过控制4向箭头控制蛇走动的游戏了。然后要解决的就是撞墙,还有吃虫,还有变长,还有吃到自己

撞墙这个必须控制,否则移动到table边缘时取不到下一个元素js本身就会报错。控制撞墙前提示也很简单,其实就是先看新舌头的坐标是否超出了0-ck的范围,再决定是结束游戏还是走一步,此例是0-30;

然后是吃虫,这个也很简单,用js取随机数的方法随机取0-ck的两个数做为虫子的坐标,如果新蛇头的坐标刚好等于虫子的坐标则表明吃到了虫子,则将虫子坐标做为新蛇头,为了增长蛇长度,后面不再挤掉蛇尾,数组不变,只是在数组尾部再加一个td,注意此处我们是依照蛇尾到蛇头顺序存入的数组,所以是数组尾部加一个新蛇头。吃掉后同时做加分操作。重新抛出一个虫子坐标。

然后是吃到自己,同样简单了,只有判断新蛇头是否包含在蛇身数组中就知道是否要吃到自己了。

然后是game over后如何结束游戏,其实就是关闭setTimeout事件,用var ji指向每一次的setTimeout结束游戏调用clearTimeout(ji)即可。

所以此时再执行移动就需要判断了,是否撞墙了,是否吃虫了,所以sheyin,shexian需要判断后再执行需要拿到if里面来:

  1. var ji = null;//控制游戏进程
  2. var mark = 0;//分数
  3. function westop(){
  4. if(ji!=null){
  5. clearTimeout(ji)
  6. }
  7. }
  8. function overItC(){//结束游戏
  9. westop();
  10. alert("吃自己了!最终得分:"+mark)
  11. }
  12. function overItZ(){//结束游戏
  13. westop();
  14. alert("撞墙了!最终得分:"+mark)
  15. }
  16. function zengIt(){//吃到虫子增长长分
  17. she[she.length] = allTds[m][n];
  18. k+=1;
  19. mark+=1;
  20. document.getElementById("mark").value = mark;//得分
  21. shiwu();
  22. }
  23. function in_array(ch){//验证是否吃到自己
  24. var inflag = true;
  25. for (var i = 0; i < she.length; i++) {
  26. if (she[i] == ch) {
  27. inflag = false;
  28. }
  29. }
  30. return inflag
  31. }
  32. function weStart(){
  33. if(dir==1){
  34. if(y+1<ck){//如果这个方向没撞墙
  35. if(in_array(allTds[x][y+1])){//如果没吃到自己
  36. sheYin(she);
  37. if(x==m&&y+1==n){//如果吃到了虫子
  38. zengIt();
  39. }else{
  40. for(var o=0;o<she.length-1;o++){
  41. she[o] = she[o+1];
  42. }
  43. she[k-1] = allTds[x][y+1];
  44. }
  45. sheXian(she);
  46. y+=1;
  47. ji = setTimeout("weStart()",sudu);
  48. }else{
  49. overItC();
  50. }
  51. }else{
  52. overItZ();
  53. }
  54. }
  55. if(dir==2){
  56. if(y-1>=0){
  57. if(in_array(allTds[x][y-1])){
  58. sheYin(she);
  59. if(x==m&&y-1==n){//如果吃到了虫子
  60. zengIt();
  61. }else{
  62. for(var o=0;o<she.length-1;o++){
  63. she[o] = she[o+1];
  64. }
  65. she[k-1] = allTds[x][y-1];
  66. }
  67. sheXian(she);
  68. y-=1;
  69. ji = setTimeout("weStart()",sudu);
  70. }else{
  71. overItC();
  72. }
  73. }else{
  74. overItZ();
  75. }
  76. }
  77. if(dir==3){
  78. if(x-1>=0){
  79. if(in_array(allTds[x-1][y])){
  80. sheYin(she);
  81. if(x-1==m&&y==n){//如果吃到了虫子
  82. zengIt();
  83. }else{
  84. for(var o=0;o<she.length-1;o++){
  85. she[o] = she[o+1];
  86. }
  87. she[k-1] = allTds[x-1][y];
  88. }
  89. sheXian(she);
  90. x-=1;
  91. ji = setTimeout("weStart()",sudu);
  92. }else{
  93. overItC();
  94. }
  95. }else{
  96. overItZ();
  97. }
  98. }
  99. if(dir==4){
  100. if(x+1<30){
  101. if(in_array(allTds[x+1][y])){
  102. sheYin(she);
  103. if(x+1==m&&y==n){//如果吃到了虫子
  104. zengIt();
  105. }else{
  106. for(var o=0;o<she.length-1;o++){
  107. she[o] = she[o+1];
  108. }
  109. she[k-1] = allTds[x+1][y];
  110. }
  111. sheXian(she);
  112. x+=1;
  113. ji = setTimeout("weStart()",sudu);
  114. }else{
  115. overItC();
  116. }
  117. }else{
  118. overItZ();
  119. }
  120. }
  121. }

 

  1. var shan = null;//控制食物闪烁进程
  2. var m;var n//食物坐标
  3. function shiwu(){//抛出食物
  4. if(shan!=null){
  5. clearTimeout(shan)
  6. }
  7. m = Math.floor(Math.random()*ck);//取的是0到ck-1
  8. n = Math.floor(Math.random()*ck);//取的是0到ck-1
  9. if(allTds[m][n].className =="hei"){//如果取到了黑色背景的说明是蛇身重新取
  10. shiwu();
  11. }else{allTds[m][n].className ="hei";}
  12. shanshuo();
  13. }
  14. function shanshuo(){//让食物闪烁
  15. if(allTds[m][n].className =="hei"){
  16. allTds[m][n].className ="bai";
  17. }else{
  18. allTds[m][n].className ="hei";
  19. }
  20. shan = setTimeout("shanshuo()",500);
  21. }

这时基本完成了,不过还有点小问题,方向我们可以随意控制,但蛇是不能逆行反方向走的,所有控制方向这要改。开始想到的是改变方向前先判断是否逆行再去改变,后来实施后有问题,因为比如向左走时,按向下箭头,并不是蛇头立刻向下移动,而是等到这个setTimeout的0.5秒到时间后才去走,就算你设的不是0.5秒,这也是有时间间隔的,这段时间间隔内我们按了向下,非逆行改变了dir的值为下,立刻再按向右箭头,判断与原来的值下非逆行,也可改变dir为向右,但真正移动时。。。逆行了。。

解决办法是我们设一个临时的方向dirLin,无论怎么改变方向都是改变这个dirLin,真正执行移动这个操作时再去判断dirLin和正在走的方向dir是否逆行,如果不逆行,则dir=drLin,否则dir保持原方向。

最终代码如下:

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <title>qqqun21/777/12</title>
  5. <meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>
  6. <style>
  7. #she{border-collapse:collapse;}
  8. #she td{
  9. width:15px;
  10. height:15px;
  11. border:1px solid grey;
  12. }
  13. .hei{ background:#000;}
  14. .bai{ background:#ffff;}
  15. </style>
  16. <script>
  17. var allTds = new Array();
  18. var ck = 30;//地图长宽
  19. var k=3;//初始长度
  20. var she = new Array();//初始化蛇
  21. var x = 0;y = k-1;//蛇头坐标
  22. var m;var n//食物坐标
  23. var dir = 1;//方向,1前,2后,3上,4下
  24. var dirLin = 1;//临时方向,防止转圈按上左右导致逆行,1前,2后,3上,4下
  25. var ji = null;//控制游戏进程
  26. var shan = null;//控制闪烁进程
  27. var mark = 0;//分数
  28. var sudu = 500;
  29. window.οnlοad=function(){
  30. for(var i = 0;i<ck;i++){
  31. var tr = document.createElement("tr");
  32. var trs = new Array();
  33. for(var j=0;j<ck;j++){
  34. var td=document.createElement("td");
  35. trs[j] = td;
  36. tr.appendChild(td);
  37. }
  38. document.getElementById("she").appendChild(tr);
  39. allTds[i] = trs;
  40. }
  41. for(var g = 0;g< k;g++){
  42. she[g] = allTds[0][g];
  43. allTds[0][g].className = "hei";
  44. }
  45. shiwu();
  46. }
  47. function sheYin(obj){//蛇隐
  48. obj[0].className = "bai";
  49. }
  50. function sheXian(obj){//蛇现
  51. obj[obj.length-1].className = "hei";
  52. }
  53. function shiwu(){
  54. if(shan!=null){
  55. clearTimeout(shan)
  56. }
  57. m = Math.floor(Math.random()*ck);
  58. n = Math.floor(Math.random()*ck);
  59. if(allTds[m][n].className =="hei"){
  60. shiwu();
  61. }else{allTds[m][n].className ="hei";}
  62. shanshuo();
  63. }
  64. function shanshuo(){
  65. if(allTds[m][n].className =="hei"){
  66. allTds[m][n].className ="bai";
  67. }else{
  68. allTds[m][n].className ="hei";
  69. }
  70. shan = setTimeout("shanshuo()",500);
  71. }
  72. function overItC(){//结束游戏
  73. westop();
  74. alert("吃自己了!最终得分:"+mark)
  75. }
  76. function overItZ(){//结束游戏
  77. westop();
  78. alert("撞墙了!最终得分:"+mark)
  79. }
  80. function zengIt(){//吃到虫子增长长分
  81. she[she.length] = allTds[m][n];
  82. k+=1;
  83. mark+=1;//每次得1分,可自己改
  84. document.getElementById("mark").value = mark;
  85. shiwu();
  86. }
  87. function westop(){
  88. if(ji!=null){
  89. clearTimeout(ji)
  90. }
  91. if(shan!=null){
  92. clearTimeout(shan)
  93. }
  94. }
  95. function in_array(ch){//验证是否吃到自己
  96. var inflag = true;
  97. for (var i = 0; i < she.length; i++) {
  98. if (she[i] == ch) {
  99. inflag = false;
  100. }
  101. }
  102. return inflag
  103. }
  104. function westart(){
  105. if(dir==1&&dirLin!=2){
  106. dir=dirLin;
  107. }
  108. if(dir==2&&dirLin!=1){
  109. dir=dirLin;
  110. }
  111. if(dir==3&&dirLin!=4){
  112. dir=dirLin;
  113. }
  114. if(dir==4&&dirLin!=3){
  115. dir=dirLin;
  116. }
  117. if(dir==1){
  118. if(y+1<ck){
  119. if(in_array(allTds[x][y+1])){
  120. sheYin(she);
  121. if(x==m&&y+1==n){//如果吃到了虫子
  122. zengIt();
  123. }else{
  124. for(var o=0;o<she.length-1;o++){
  125. she[o] = she[o+1];
  126. }
  127. she[k-1] = allTds[x][y+1];
  128. }
  129. sheXian(she);
  130. y+=1;
  131. ji = setTimeout("westart()",sudu);
  132. }else{
  133. overItC();
  134. }
  135. }else{
  136. overItZ();
  137. }
  138. }
  139. if(dir==2){
  140. if(y-1>=0){
  141. if(in_array(allTds[x][y-1])){
  142. sheYin(she);
  143. if(x==m&&y-1==n){//如果吃到了虫子
  144. zengIt();
  145. }else{
  146. for(var o=0;o<she.length-1;o++){
  147. she[o] = she[o+1];
  148. }
  149. she[k-1] = allTds[x][y-1];
  150. }
  151. sheXian(she);
  152. y-=1;
  153. ji = setTimeout("westart()",sudu);
  154. }else{
  155. overItC();
  156. }
  157. }else{
  158. overItZ();
  159. }
  160. }
  161. if(dir==3){
  162. if(x-1>=0){
  163. if(in_array(allTds[x-1][y])){
  164. sheYin(she);
  165. if(x-1==m&&y==n){//如果吃到了虫子
  166. zengIt();
  167. }else{
  168. for(var o=0;o<she.length-1;o++){
  169. she[o] = she[o+1];
  170. }
  171. she[k-1] = allTds[x-1][y];
  172. }
  173. sheXian(she);
  174. x-=1;
  175. ji = setTimeout("westart()",sudu);
  176. }else{
  177. overItC();
  178. }
  179. }else{
  180. overItZ();
  181. }
  182. }
  183. if(dir==4){
  184. if(x+1<30){
  185. if(in_array(allTds[x+1][y])){
  186. sheYin(she);
  187. if(x+1==m&&y==n){//如果吃到了虫子
  188. zengIt();
  189. }else{
  190. for(var o=0;o<she.length-1;o++){
  191. she[o] = she[o+1];
  192. }
  193. she[k-1] = allTds[x+1][y];
  194. }
  195. sheXian(she);
  196. x+=1;
  197. ji = setTimeout("westart()",sudu);
  198. }else{
  199. overItC();
  200. }
  201. }else{
  202. overItZ();
  203. }
  204. }
  205. }
  206. function keyDown(event){//接受键盘改变方向
  207. if(event.keyCode==39){//左箭头
  208. dirLin =1;
  209. }
  210. if(event.keyCode==37){//右箭头
  211. dirLin =2;
  212. }
  213. if(event.keyCode==38){//上箭头
  214. dirLin =3;
  215. }
  216. if(event.keyCode==40){//下箭头
  217. dirLin =4;
  218. }
  219. }
  220. </script>
  221. </head>
  222. <body οnkeydοwn="keyDown(event)">
  223. <table cellpadding="0" cellspacing="0" id="she" align="center"></table>
  224. <table cellpadding="0" cellspacing="0" width="300px;" align="center">
  225. <tr>
  226. <td><input type="button" value="start" οnclick="westart()"/></td>
  227. <td>得分:<input id="mark" type="text" value="0" readonly size=3/></td>
  228. <td><input type="button" value="stop" οnclick="westop()"/></td>
  229. </tr>
  230. </table>
  231. </body>

欢迎交流学习

Java学习交流群: 2177712

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/1002537
推荐阅读
相关标签
  

闽ICP备14008679号