主要有五个知识点,一一道破,首先是监听键盘事件:
<!DOCTYPE html> <head> <meta charset=utf-8 /> <title>贪吃蛇游戏</title> </head> <body> <h1>按键事件</h1> <div id="keypress"></div> <div id="keydown"></div> <div id="keyup"></div> <script> document.onkeypress = function(e) { document.getElementById('keypress').innerText = 'press : ' + e.keyCode + ' ' + e.which + ' ' + e.charCode; } document.onkeydown = function(e) { document.getElementById('keydown').innerText = 'down : ' + e.keyCode + ' ' + e.which + ' ' + e.charCode; } document.onkeyup = function(e) { document.getElementById('keyup').innerText = 'up : ' + e.keyCode + ' ' + e.which + ' ' + e.charCode; } </script> </body>check the result
按键相关的东西就是三个:document.onkeypress、document.onkeydown、document.onkeyup,分别表示字符按键、按下和弹起,键的信息都在参数e中,onkeypress只有按到字符键才有效,onkeydown和onkeyup基本对所有键都有效,所以贪吃蛇的上下左右用onkeydown就好。
接下来是每隔一定时间就触发动作,其实专业术语叫定时器:
<!DOCTYPE html> <head> <meta charset=utf-8 /> <title>贪吃蛇游戏</title> </head> <body> <h1>定时器</h1> <button id="start">开始</button> <button id="stop">停止</button> <div id="num"></div> <script> var i = 0; var timer = undefined; document.getElementById('start').onclick = function() { timer = setInterval(function(){ i++; document.getElementById('num').innerText = i; }, 500); document.getElementById('start').disabled = true; document.getElementById('stop').disabled = false; }; document.getElementById('stop').onclick = function() { if(timer) { clearInterval(timer); timer = undefined; } document.getElementById('start').disabled = false; document.getElementById('stop').disabled = true; }; document.getElementById('stop').disabled = true; </script> </body>check the result
这里的重点是setInterval函数,第一个参数是回调函数,第二个参数是时间间隔,单位ms,这个函数会返回一个timer,代表定时器,当我们要取消定时器的时候,就调用clearInterval,将setInterval返回的timer作为参数传进去。还有就是可以通过设置元素的disabled为true或false来控制按钮的可点击状态。
再接下来就是随机数了,这个也很简单:
<!DOCTYPE html> <head> <meta charset=utf-8 /> <title>贪吃蛇游戏</title> </head> <body> <h1>产生1~100的随机数</h1> <button id="btn">产生随机数</button> <div id="num"></div> <script> document.getElementById('btn').onclick = function() { var ran = Math.random(); var res = Math.ceil(ran * 100); document.getElementById('num').innerText = res; }; </script> </body>check the result
Math.ramdon产生0~1之间的随机数,但是是小数,要把它变成1~100的整数,只需要乘以100,再向上去整就好了。
然后是二维地图的实现:
<!DOCTYPE html> <head> <meta charset=utf-8 /> <title>贪吃蛇游戏</title> </head> <style> .map { width : 400px; height : 400px; border : 5px solid brown; background-color : transparent; position : relative; } .snakehead, .snake, .target { width : 20px; height : 20px; position : absolute; } .snakehead { background-color : pink; } .snake { background-color : red; } .target { background-color : blue; } </style> <body> <h1>二维地图</h1> <div class="map"> <div class="snakehead" style="top:0px;left:0px;"></div> <div class="snake" style="top:20px;left:0px;"></div> <div class="snake" style="top:40px;left:0px;"></div> <div class="snake" style="top:60px;left:0px;"></div> <div class="target" style="top:60px;left:60px;"></div> </div> </body>check the result
这里用了一个relative的div作为map,然后里面蛇和食物都是absolute的div,移动的时候只要设置它们的left和top就行了。注意这里的样式设置,.snakehead, .snake, .target就可以设置.snakehead、.snake和.target的公共样式。
最后是动态添加蛇身:
<!DOCTYPE html> <head> <meta charset=utf-8 /> <title>贪吃蛇游戏</title> </head> <style> .map { width : 400px; height : 400px; border : 5px solid brown; background-color : transparent; position : relative; } .snake { width : 20px; height : 20px; position : absolute; background-color : red; } </style> <body> <h1>动态添加DOM元素</h1> <button id="add">添加</button> <div class="map" id="map"></div> <script> var myLeft=0; var myTop=0; document.getElementById('add').onclick = function() { if(myTop < 400) { var newNode = document.createElement('div'); newNode.className = 'snake'; newNode.style.top = myTop + 'px'; newNode.style.left = myLeft + 'px'; document.getElementById('map').appendChild(newNode); myLeft += 20; if(myLeft >= 400) { myLeft = 0; myTop += 20; if(myTop >= 400) { window.alert('满了'); } } } }; </script> </body>check the result
首先我们用document.createElement来创建一个div,然后设置它的一些属性,最后用document.getElementById('map').appendChild(newNode),把它加到map中,这样就动态添加了一个div。
好了,基本的知识点都已经打通,接下来就打大BOSS了:
<!DOCTYPE html> <head> <meta charset=utf-8 /> <title>贪吃蛇游戏</title> </head> <style> .map { width : 400px; height : 400px; border : 5px solid brown; background-color : transparent; position : relative; } .snakehead, .snake, .target { width : 20px; height : 20px; position : absolute; } .snakehead { background-color : pink; } .snake { background-color : red; } .target { background-color : blue; } </style> <body> <h1>贪吃蛇</h1> <div class="map" id="map"></div> <script> // 0:up 1:left 2:down 3:right var dir = 3; var step = [{dx : 0, dy : -20}, {dx : -20, dy : 0}, {dx : 0, dy : 20}, {dx : 20, dy : 0}]; // the snake, a array of div var snake = []; // the snake target var target; // timer var timer = undefined; // can not act two key in one interval var isKey = false; // add snake var add = function(left, top, className) { var newNode = document.createElement('div'); newNode.className = className; newNode.style.left = left + 'px'; newNode.style.top = top + 'px'; document.getElementById('map').appendChild(newNode); snake.push(newNode); }; // judge the target in snake var inSnake = function(newx, newy, type) { var start = 0; if(type == 'all') { start = 0; } else if(type == 'body') { start = 1; } for (var i = start; i<snake.length; i++) { if(parseInt(snake[i].style.left) == newx && parseInt(snake[i].style.top) == newy) { return true; } } return false; }; // new target var newTarget = function() { var newx = -1; var newy = -1; while(newx == -1 || newy == -1 || inSnake(newx, newy, 'all')) { newx = (Math.floor(Math.random()*20) * 20); newy = (Math.floor(Math.random()*20) * 20); } target.style.left = newx + 'px'; target.style.top = newy + 'px'; }; // snake move var move = function() { // calculate next head pos var nextx, nexty; nextx = parseInt(snake[0].style.left) + step[dir].dx; nexty = parseInt(snake[0].style.top) + step[dir].dy; // is Game Over? if(nextx < 0 || nextx > 380 || nexty < 0 || nexty > 380) { clearInterval(timer); window.alert('Game Over!'); return; } // move var tailLeft = snake[snake.length-1].style.left; var tailTop = snake[snake.length-1].style.top; for(var i=snake.length-1; i>0; i--) { snake[i].style.left = snake[i-1].style.left; snake[i].style.top = snake[i-1].style.top; } snake[0].style.left = nextx + 'px'; snake[0].style.top = nexty + 'px'; // is get food? if(nextx == parseInt(target.style.left) && nexty == parseInt(target.style.top)) { add(parseInt(tailLeft), parseInt(tailTop), 'snake'); newTarget(); } // snake head meet snake body if(inSnake(parseInt(snake[0].style.left), parseInt(snake[0].style.top), 'body')) { clearInterval(timer); window.alert('Game Over!'); return; } isKey = false; } add(60, 20, 'snakehead'); add(40, 20, 'snake'); add(20, 20, 'snake'); target = document.createElement('div'); target.className = 'target'; newTarget(); document.getElementById('map').appendChild(target); // key event document.onkeydown = function(e) { if(!isKey) { if(e.keyCode == 38) { //up if(dir != 2) {dir = 0; isKey = true;} } else if(e.keyCode == 39) { //right if(dir != 1) {dir = 3; isKey = true;} } else if(e.keyCode == 40) { //down if(dir != 0) {dir = 2; isKey = true;} } else if(e.keyCode == 37) { //left if(dir != 3) {dir = 1; isKey = true;} } } }; // timer timer = setInterval(move, 100); </script> </body>check the result
这里用了snake作为一个数组,每个元素是一个div的Element,然后就是游戏结束的条件,有两个,一个是蛇碰到墙壁,一个是蛇碰到自身,主要逻辑就是蛇吃到食物的时候,蛇身要变长,然后食物随机换一个位置,当然不能与蛇身重合。当然游戏的细节比较多的,可以结合代码理解。
这个游戏调了两天,虽然规则很简单,但是细节还是需要花费精力的。