手把手教你写贪吃蛇

手把手教你写贪吃蛇👩‍🔧原生JS

超详细的教程,一步一步和我来,你一定会学会的~

绘制界面

设置分数和速度

    <h1>score:</h1>
    <button></button>
    <button></button>
    <button></button>

分数字体大,采用h1

速度需要用户点击按钮进行选择,所以采用button

画运动界面

  • 用表格显示格子,作为刻度(十行十列),每个格子的宽为40px,高为40px;

table>tr*10>th*10; 快捷方式生成十行十列的表格

​ 样式:

​ ①在html的table标签中加入样式:

​ border=“1” (表格的边框长度为1px) cellpadding=“0”(表格的内边距为0px)                cellspacing="0"这个别忘了设定,因为我这里默认是2px,就导致对不齐

​ ②在css中给每一个td都设置长和宽为38px(因为border四边还有1px占用

​ ③position:absolute 使表格浮动起来,不占用空间,以便div重叠上去

用一个大div设置实际运动场所

用一个大的div来供JS插入新的div(主战场)

样式:

①宽高:均为400px

②背景颜色也是在div上设置,不是在表格上设置

封装产生div的方法

通过在大的div后面附加子div的方式,来显示蛇头,蛇身子和食物。

封装一个函数还创建小div

 var map=document.getElementById("map");
        function creatDiv(color){
            var div = document.createElement("div");
            map.appendChild(div);
        }

给小div设置大小

  function creatDiv(color){
            var div = document.createElement("div");
            div.style.width="38px";
            div.style.height="38px";
            map.appendChild(div);
        }

给小div根据传入参数的不同而设置颜色的不同

  var map=document.getElementById("map");
        function creatDiv(color){
            var div = document.createElement("div");
            div.style.width="40px";
            div.style.height="40px";
            div.style.backgroundColor=color;
            map.appendChild(div);
        }
        creatDiv("red"); //创建一个蛇头
        creatDiv("red"); //创建一个食物

当创建好蛇头和食物后,要让他们的位置也变成absolute。目的是要让他们浮动起来,并且如果生成在同一行的时候,一个不会把另一个挤下去

div.style.position="absolute";

利用随机数产生蛇头和食物div

Math.random();随机产生一个[0,1)的随机数

我们的要求:div的left和top应该位于[0,360]之间,并且必须是40的倍数

方法:

Math.random()*10 =>产生一个[0,10)的随机数,即[0,9]的随机数。

parseInt(Math.random()*10) =>将其转化为[0,9]的整数

parseInt(Math.random()*10) *40 =>产生一个[0,360],且为40的倍数的整数

       div.style.left = parseInt(Math.random() * 10) * 40 + "px";
            div.style.top = parseInt(Math.random() * 10) * 40 + "px";

利用返回值得到通过函数创建的div

因为之后要获取蛇头和食物的位置,但因是在函数中创建的,所以函数已结束就临时变量销毁了,无法获取,所以通过设置函数的返回值为div,并var一个div承接,来得到蛇头和食物

 function creatDiv(color) {
            XXXXX
            return div;
        }
        var headNode = creatDiv("red");
        var foodNode = creatDiv("blue");

利用定时器让蛇头移动起来

给headNode一个value属性,规定他的移动方向(默认初始为向上移动)

headNode.value="up";

利用定时器函数,让蛇头移动(move是个移动的函数)

var t = setInterval(move, 1000);

定义move函数(注意这里的变换坐标形式)

 function move() {
            switch (headNode.value) {
                case "up":
                    headNode.style.top = parseInt(headNode.style.top) - 40 + "px";
                    break;
                case "down":
                    headNode.style.top = parseInt(headNode.style.top) + 40 + "px";
                    break;
                case "right":
                    headNode.style.left = parseInt(headNode.style.left) + 40 + "px";
                    break;
                case "left":
                    headNode.style.left = parseInt(headNode.style.left) - 40 + "px";
                    break;
            }
        }

通过键盘的按键来改变蛇头的移动方向

  • 利用键盘按下事件: document.onkeydown()。利用上下左右键来更改蛇头的移动方向

  • 通过获取键盘的键值,对应不同的方向,那应该怎么获取呢?

    在本例的事件编程中,由document.onkeydown可得:事件源是我的键盘,事件就是键盘按下。function(){ }是事件处理函数。在事件处理函数当中其实存在一个默认的参数event(可写可不写,用的时候需要写),表示事件对象,所以获取键盘的键值,就可以通过event.keyCode来获取

    document.onkeydown=function(event){
     event=event||window.event;
    }
    
  • 监听键值,并根据键盘上敲击的键更改蛇头的移动方向

    并且,在蛇有身体之后,不能让蛇头在向一个方向移动的时候向相对方向移动(自噬)。

    document.onkeydown = function (event) {
                event = event || window.event;
                switch (event.keyCode) {
                    case 37:
                        if(headNode.value!="right"||arr.length==0)
                        headNode.value = "left";
                        break;
                    case 38:
                        if(headNode.value!="down" || arr.length == 0)
                        headNode.value = "up";
                        break;
                    case 39:
                        if(headNode.value!="left" || arr.length == 0)
                        headNode.value = "right";
                        break;
                    case 40:
                        if(headNode.value!="up" || arr.length == 0)
                        headNode.value = "down";
                        break;
    
                }
            }
    

检测是否吃到食物并改变食物位置

  • 首先需要思考把监测步骤放到哪里?

    应该放到move函数里,每移动一次就判断时候吃到食物

  • 如何判断呢?

    当蛇头的top等于食物的top,蛇头的left等于食物的left时,就相等于吃到了食物

  • 如果吃到食物后该怎么更新食物的位置呢?

    我本来想的是利用原来createDiv函数重新生成一个div,但是这样不仅执行的代码量多,而且还需要删除原来的食物div,所以正确方法应该是重新刷新食物的top和left值即可

    function move() {
                switch (headNode.value) {
                   xxxx...
                }
                if(foodNode.style.top==headNode.style.top&& foodNode.style.left == headNode.style.left){
                    foodNode.style.top ==  parseInt(Math.random() * 10) * 40 + "px";
                    foodNode.style.left == parseInt(Math.random() * 10) * 40 + "px";
                }
            }
    

产生新的身体

  1. 通过什么产生新的身体? createNode函数

    var newNode=createNode("yellow");
    
  2. 将新身体跟在什么的后面? 跟在最后一节的后面。

    所以🐍的身体应该用数组保存。

    在全局中: var arr=[];//保存身体的数组

    还要分情况讨论:

    如果只有一个蛇头,最后一节就是蛇头

    如果蛇头后面还有身体,即length>0,通过arr[length-1]来获取蛇的最后一节

    //吃到了食物
         if (foodNode.style.top == headNode.style.top && foodNode.style.left == headNode.style.left) {
                    foodNode.style.top = parseInt(Math.random() * 10) * 40 + "px";
                    foodNode.style.left = parseInt(Math.random() * 10) * 40 + "px";
                var newNode=creatDiv("yellow");
                var lastNode;
                    //得到最后一节
                    if(arr.length>0){
                        lastNode=arr[length-1];
                    }
                    else{
                        lastNode=headNode;
                    }
                }
    
  3. 给新的一节设置value(移动方向)

    新的一节的移动方向应该和lastNode的移动方向相同

    newNode.value=lastNo
    
  4. 将新身体也附在身体数组中

                arr.push(newNode);
    

根据最后一块的运动方向确定新产生一节的位置

  1. 如何确定新产生的一节的位置? 需要找到最后一节的移动方向,如果向右,新的一节在它左侧出现,如果向左,新的一节在他右侧出现

  2. 如何找到最后一节的移动方向? 找到最后一节的value

    如果最后一节向右,那么如何让新的一节在最后一节的左侧出现? 让newNode的left = lastNode的left + 40px, 新一节的top和最后一节的top相同。其他方向同理。

身体的移动

  • 身体移动写在哪?

    身体移动也是移动的一部分,所以应该写在move函数中

  • 是先让蛇头移动还是先让身体移动?

    先让身体移动

  • 每一节应该是先移动身体再调整方向,还是想调整方向再移动身体?

    因为每一节身体应该与他上一节上一次的运动方向相同。所以应该是先移动身体(本次移动的时候的方向还是和上一节上一次移动方向保持一致),在调整方向。

  • 要移动每一节,应该用for循环遍历身体arr数组,那遍历时是从第一节开始还是从最后一节开始?

    如果从第一节开始,那么会出现:第一节的运动方向=头,第二节的运动方向=第一节=头,即所有的节的运动方向都等于头的运动方向,所以不对。应该是从最后一节开始遍历。

所以,整个蛇的移动应该是从最后一节开始,不断向前的。

function move() {
            //遍历整个蛇身子,从最后一节开始
            for(var i=arr.length-1;i>=0;i--){
                //蛇的一节移动
                switch (arr[i].value) {
                    case "up":
                        arr[i].style.top = parseInt(arr[i].style.top) - 40 + "px";
                        break;
                    case "down":
                        arr[i].style.top = parseInt(arr[i].style.top) + 40 + "px";
                        break;
                    case "right":
                        arr[i].style.left = parseInt(arr[i].style.left) + 40 + "px";
                        break;
                    case "left":
                        arr[i].style.left = parseInt(arr[i].style.left) - 40 + "px";
                        break;
                }
                //蛇的一节调整方向,方向与蛇的上一节的上一次的移动方向一致
                if(i==0){
                    arr[i].value=headNode.value;
                }
                else{
                    arr[i].value=arr[i-1].value;
                }
            }
            
            //蛇头移动
            switch (headNode.value) {
               xxxx....
            }
            
            //移动过程中吃到了食物
            if (foodNode.style.top == headNode.style.top && foodNode.style.left == headNode.style.left) {
                var newNode=creatDiv("yellow"); //身体增加一节
                var lastNode;
                //得到最后一节 
                //给新节点的移动方向赋值
                newNode.value = lastNode.value;
                //通过原来最后一个节点的方向更改新节点的位置
                //将新的一节附在身体数组
                //更新食物位置     
            }
        }

判断蛇死亡

情况1:超出边界,撞墙死

情况2:碰到自己的身体,即for循环判断每一块身体是否和头重合

 if (parseInt(headNode.style.top) < 0 || parseInt(headNode.style.left) < 0 || parseInt(headNode.style.top) > 360 || parseInt(headNode.style.left) > 360) {
                clearInterval(t);
                alert("蛇撞墙了,游戏结束");
            }
            //碰到自己身体死了
            if (arr.length > 0) {
                for (var i = 0; i < arr.length; i++) {
                    if (arr[i].style.top == headNode.style.top && arr[i].style.left == headNode.style.left) {
                        clearInterval(t);
                        alert("蛇碰到自己身体了,游戏结束");
                    }
                }
            }

更新食物位置的时候防止其在身体部位生成

 //更新食物位置
                px= parseInt(Math.random() * 10) * 40 + "px";
                py= parseInt(Math.random() * 10) * 40 + "px";
               //遍历整个身子
                for(var i=0;i<arr.length;i++){
                    //如果身子的某一节和食物
                    if(parseInt(arr[i].left)==px&&parseInt(arr[i].top)==py){
                        //重新生成蛇的位置
                        px = parseInt(Math.random() * 10) * 40 + "px";
                        py = parseInt(Math.random() * 10) * 40 + "px";
                        //重置i重新从头判断
                        i=-1;
                    }
                }
                foodNode.style.left =px+"px";
                foodNode.style.top=py+"px";

吃到食物后增加分数

html中:

<span>
        <h1 id="Score">score:</h1>
</span>

设置全局变量:

        var score = 0;//计分
        var span = document.getElementById("Score");

吃到食物后:

score += 10;
                span.innerHTML = "<h1>Score:" + score + "</h1>";

点击按钮控制速度

js中以点击按钮事件传入参数进行控制速度:

/将不变的速度改成传参可以控制的速度,绑定按钮进行传参
原来:
 var t = setInterval(move, 500);
现在:
 function changeTime(speed){
            var t = setInterval(move, speed);
        }

html中:

<button onclick="changeTime('200')">快</button>
    <button onclick="changeTime('300')">中</button>
    <button onclick="changeTime('500')">慢</button>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值