微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

js 原生贪吃蛇 ES6训练类和对象

// 面向对象思想,利用对象解决贪吃蛇
//1.地图
//2.食物
//3.蛇

map.js

    //利用面向对象思想创建地图
    class Map{
        constructor(){
            //创建地图
            let oMap = document.createElement("div");
            //设置地图
            oMap.className = "map_all"
            /* oMap.style.position = relative; */
            //把地图添加页面中
            document.body.appendChild(oMap);
            //拿到其中属性
            let oMapStyle  = window.getComputedStyle(oMap)
            this.oMap  = oMap;
            this.width = oMapStyle.width
            this.height = oMapStyle.height
        }
    }
        
    
    //只需创建Map的实例对象,即可创建一个地图

snake.js

//Snake:1.创建蛇各部分并渲染到页面
//       2. 
class Snake{
    constructor(obj){
        let snake_all = [
            {x:2,y:1,type:1},
            {x:1,y:1,type:0},
            {x:0,y:1,type:0},
        ]
        //没有设置就使用认值,注意obj认值是空对象必须写,否则下方设置没用,因为均是设置给obj的属性
        obj = obj || { }
        this.width   = obj.width   || 100
        this.height  = obj.height  || 100
        this.bodyimg = obj.bodyimg || "images/body.png"
        this.headimg = obj.headimg || "images/head.png"
        this.snake_all = snake_all;
        this.map = obj.map || {}
        //键盘按键按下事件,仅执行一次,作为蛇头移动的参考
        document.body.onkeydown = (event)=>{
            //由于下方需要蛇头移动的按键,作为属性传入key
            //使用es6箭头函数解决this为body添加不到构造函数属性的问题,使用箭头函数this即为当前的父作用域
            this.key = event.key;

        };
        //判断边界最大数值使用
        let widthNumber = parseInt(map.width)  / parseInt(this.width);
        let heightNumber = parseInt(map.height)  / parseInt(this.height);
        this.widthNumber = widthNumber;
        this.heightNumber = heightNumber;
    }
    creatSnake(map){

        //由于每次移动后都要重新渲染,因此要将以前的蛇部分全部清除,为了方便操作,为其蛇部分添加类名标识,querySelectorAll找到所有的
            let snake_parts = document.querySelectorAll('.snake_parts')
            //console.log(snake_parts),不加定时器输出两次,而且它返回的是一个数组,使用for of遍历
            for(let value of snake_parts){
                value.parentNode.removeChild(value)
            }
            //同样的,数组也要变为属性,要不原型对象中找不着
        for(let value of this.snake_all){
            //for of 方法专门遍历数组并进行操作
            //1.创建每一部分对应的div
            let oDiv = document.createElement('div')
            //2.设置蛇属性
            oDiv.style.width = this.width +"px"
            oDiv.style.height = this.height +"px"
            //3.设置蛇每部分的位置,前提设置poa,否则没法设置偏移量
            oDiv.style.position =  "absolute"
            oDiv.style.left = value.x * parseInt(this.width)  + "px";
            oDiv.style.top =  value.y * parseInt(this.height) + "px";
            //4.设置蛇每部分对应背景
            if(value.type==1){  //都写到模板字符串内,不要 url(` `)
                oDiv.style.background = `url(${this.headimg})`
            }
            else {
                oDiv.style.background = `url(${this.bodyimg})`
            }
            //5.将蛇的每一部分均添加到地图中
            map.oMap.appendChild(oDiv)
            //6.为蛇每部分添加类名标识,便于找到遍历删除
            oDiv.className="snake_parts"
        }  
    }
    move(){
            //1. 移动蛇身,后一个身体组件的位置移动到前一个身体组件的位置,则最前方与头重叠,单独设置头移动即可,因此从后往前循环
            for(let i=this.snake_all.length-1;i>0;i--){
                //这里的移动不包括蛇头
                    //后一个位置x,y移到前一个的位置x,y,调整的是x,y而非整个部分
                    this.snake_all[i].x =  this.snake_all[i-1].x
                    this.snake_all[i].y =  this.snake_all[i-1].y
                    //要单独设置调整后的type,因为最前面的蛇身体与蛇头重叠了
                    this.snake_all[i].type = 0;
            }

            //2. 移动蛇头,根据按键方向来调整蛇头方向与位置,因此需要先获取到蛇头,而且不能写到最上方,因为蛇头每次都在变化,必须写到定时器内
            let snakehead = this.snake_all[0]
            //console.log(this.key)undefiend,是因为上方按键事件用的es5,this为body,而不是整个构造函数,因此么有值,蛇头也动不了,因此换成箭头函数
            switch(this.key){
                //向上
                case "w": snakehead.y = snakehead.y-1
                    break;
                //向下
                case "s": snakehead.y = snakehead.y+1
                    break;
                //向左
                case "a": snakehead.x = snakehead.x-1
                    break;
                //向右
                case "d": snakehead.x = snakehead.x+1
                    break;
                default: snakehead.x = snakehead.x+1
                break;
            }
    }
    inspection(snakefood){
        //检测边界代码
         //3.判断蛇头是否超过边界,没有超过继续定时器内删除创建,超过不渲染蛇,并清除定时器
         let snakehead = this.snake_all[0]
         if(snakehead.x<0||snakehead.y<0||snakehead.x>=this.widthNumber||snakehead.y>=this.heightNumber){
            alert("不要超出挑战区,否则xz鸽鸽去找你玩耍~")
            clearInterval(this.timer)
            //超出边界了不绘制,不超出认在外部返回true;
            return false;
        }
        //5.每次移动时都要判断是否吃到了豆豆(蛇头x,y与豆豆一样),吃到了要变长一截,然后把场上的豆豆删掉,重新渲染一个豆豆;
            //console.log(snakefood.oFood.style.left,snakefood.oFood.style.top,snakehead.x,snakehead.y) 
            // 700px 400px 7 4 因此要进行判断,可以先取整再/长度 .即为 7 4 7 4
            if(snakehead.x===parseInt(snakefood.oFood.style.left)/this.width  && snakehead.y===parseInt(snakefood.oFood.style.top)/this.height ){

            //吃到了就立即删除场上的食物,并重新渲染一个食物出来,怎么删除怎么渲染只有食物知道,进入食物设置方法
                    //删除场上食物
                    snakefood.removeFood()
                    //重新渲染食物
                    snakefood.xuanran(map)

                //吃到了,新建一节蛇身躯放到最后
                    //拿到最后一节的位置
                    let lastBody = this.snake_all[this.snake_all.length-1]
                    //将最后一节位置的x,y赋给新增到最后的数组,此时最后一个与新增的重叠了,因此要以最后一个为依据,单独处理最后一节身躯的x,y。
                    let newBody  = {x:lastBody.x,y:lastBody.y,type:0}
                    switch(this.key){
                        //根据朝向,设置最后一节的位置
                        //向上
                            case "w": newBody.y = newBody.y + 1
                            break;
                        //向下
                        case "s": newBody.y = newBody.y - 1
                            break;
                        //向左
                        case "a": newBody.x = newBody.x + 1
                            break;
                        //向右
                        case "d": newBody.x = newBody.x - 1
                            break;
                        default: newBody.x = newBody.x - 1
                        break;
                    }
                    //将调整好位置的最后一节身躯添加到数组snake_all中,放到蛇身体里,利用push方法放到最后,可以一直拿到最后新的数值,即可循环起来
                    this.snake_all.push(newBody)
                    
    }
    //没有超出边界认返回true
    return true;    
}
    updata(map,snakefood){  
        //定时器要定义同样为属性,否则clear删除不掉,直接给名不好使
      this.timer = setInterval(() => { 
        //1.让蛇移动起来 
        this.move()
        //2.检测边界与食物,单独设置inspection方法,在updata总方法内这里会给inspection传递snakefood参数,这样照样执行this.inspection(snakefood)
       let flag = this.inspection(snakefood)
       if(!flag){
           //如果flag为假,即超出了边界,则不绘制蛇了,不往下面执行creatSnake,直接return退出
           return;
       }
        //3.蛇的位置变了,重新渲染蛇的每一部分,仍需要map,可以在上面的方法将其作为构造函数一个属性,构造函数内的所有方法即可使用map
            this.creatSnake(map)
        }, 500);
    }
}

snakefood.js

    //利用面向对象思想创建随机生成的食物
    class SnakeFood{
        constructor(){
            //创建食物div
            let oFood = document.createElement("div")
            //设置食物样式
            oFood.className = "oFood";
                oFood.style.width = 100+"px"
                oFood.style.height = 100+"px"
            //获取当前食物div宽高
                //style只能获取行内样式,window.getComputedStyle返回元素的所有非行内样式,class
                // let oFoodstyle  = window.getComputedStyle(oFood)
                // oFoodstyle.width oFoodstyle.height 均为空 ,创造的实例中对应属性也为空,获取不到,用js设置可以拿到
            this.width1  = oFood.style.width
            this.height1 = oFood.style.height
            this.oFood  = oFood
        }   
        xuanran(map){
            //随机生成位置,为其设置位置,将设置好的位置添加到地图中去。
                //1.查看地图中最多容纳几个食物
                    //同理,想拿到map.width,也不能直接style拿,都是用非行内,window.getComputedStyle,发现报错,拿不到this.width1,用js
               let widthNumber = parseInt(map.width)  / parseInt(this.width1);
               let heightNumber = parseInt(map.height)  / parseInt(this.height1);
               this.widthNumber = widthNumber;
               this.heightNumber = heightNumber;
               //2.随机生成 (0 至 最大容纳数-1)的数值,因为最终位置的改变是设置食物的left,top而决定的,0才是第一个坑位,用随机数*宽度高度赋值给偏移量即可
                                                                //用同一个构造函数中原型对象的其他方法要用this.方法,设置left,top最后要加上px
                this.oFood.style.left = parseInt(this.width1) * this.getwidthrandom()+"px"
                this.oFood.style.top = parseInt(this.height1) * this.getheightrandom()+"px"

            //添加到地图中,注意这里用保存给map属性时,不要 map.this.属性,直接map.属性用就行
                //不同方法内,ES6此处写在其构造函数的原型对象中,自然找不到oFood,也要在上方添加属性即可解决问题。
               map.oMap.appendChild(this.oFood);
        }
        getwidthrandom(){
            return Math.floor(Math.random() * ((this.widthNumber-1) - 0)) + 0; //不含最大值,含最小值
        }
        getheightrandom(){
            return Math.floor(Math.random() * ((this.heightNumber-1) - 0)) + 0; //不含最大值,含最小值
        }
        removeFood(){
            //拿到食物div并删除他
            this.oFood.parentNode.removeChild(this.oFood)
        }
    }
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <Meta charset="UTF-8">
    <Meta http-equiv="X-UA-Compatible" content="IE=edge">
    <Meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/index.css">
    <script src="./js/index.js"></script>
    <script src="./js/map.js"></script>
    <script src="./js/SnakeFood.js"></script>
    <script src="./js/snake.js"></script>
</head>

<body>
    <!-- <div class="map_all"></div> -->
    <script>
        //注意,这些创建实例均为js操作,写到script标签里;
            //创建地图
            let map =  new Map()
            //创建食物
            let snakefood = new SnakeFood();
            //随机生成食物并渲染到地图中,并记住,谁需要就放到哪个方法的参数中去
            snakefood.xuanran(map)
            //创建蛇
            let obj = {
                //这里不要加单位,因为creatSnake方法内拼接了 “px”
                width:100,
                height:100,
                bodyimg:"images/body.png",
                headimg:"images/head.png",
                map:map
            }
            let snake = new Snake(obj)
            //创建蛇身体的每个部分并渲染到地图中。
            snake.creatSnake(map)
            //蛇移动
            snake.updata(map,snakefood)
            
    </script>
</body>
</html>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐