原生js实现贪吃蛇大作战案例

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44253336/article/details/100239008

贪吃蛇大作战面向对象的编程思想:
地图: 宽,高,背景颜色,因为小蛇和食物都是相对于地图显示的,这里小蛇和食物都是地图的子元素,随机位置显示,脱离文档流的,地图也需要脱离文档流–css需要设置:宽,高,背景颜色,脱标。

食物—div元素
elements—>存储div的数组(将来删除的食物div时候,先从map中删除div,再从数组中移除div)
食物:宽,高,背景颜色,横坐标,纵坐标
一个食物就是一个对象,这个对象有相应的属性,这个对象需要在地图上显示
最终要创建食物的对象,先 有构造函数,并且把相应的值作为参数传入到构造函数中
食物要想显示在地图上,食物的初始化就是一个行为
1.食物的构造函数—>创建食物对象
2.食物的显示的方法–>通过对象调用方法,显示食物,设置相应的样式
2.1.1 因为食物要被小蛇吃掉,吃掉后应该再次出现食物,原来的食物就删除了
2.1.2 每一次初始化食物的时候先删除原来的食物,然后重新的初始化食物
2.1.3 通过一个私有的函数(外面不能调用的函数)删除地图上的食物,同时最开始的时候食物也相应的保存到一个数组中,再从这个数组中把食物删除
最后的时候,把食物的构造函数给window下的属性,这样做,外部就可以直接使用这个食物的构造函数了。

小蛇
小蛇就是一个对象
属性: 每个身体都有宽,高,方向
属性:身体分三个部分,每个部分都是一个对象,每个部分都有横纵坐标,背景颜色
小蛇要想显示在地图上,先删除之前的小蛇,然后再初始化小蛇(小蛇要移动)–方法

小蛇要移动—方法
思路:把小蛇的头的坐标给小蛇第一部分的身体,第一部分的身体的坐标给下一个部分身体
小蛇的头,需要单独的设置:方向

源代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .map {
            width: 800px;
            height: 600px;
            background-color: #ccc;
            position: relative;
        }

    </style>
</head>
<body>
<!--画出地图,设置样式-->
<div class="map"></div>

<script>


  //自调用函数----食物的
  (function () {
    var elements = []; //用来保存每个小方块食物的
    //食物就是一个对象,有宽高,有颜色,有横纵坐标。得先定义构造函数,然后创建对象(实例化)
    function Food(x, y, width, height, color) {
      //横纵坐标
      this.x = x || 0;
      this.y = y || 0;
      //宽和高
      this.width = width || 20;
      this.height = height || 20;
      //背景颜色
      this.color = color || "green";
    }

    //为原型添加初始化的方法(作用:在地图上显示这个食物)
    //因为食物需要在地图上显示,所以需要地图这个参数(map——就是页面上class=map的这个div)
    Food.prototype.init = function (map) {
      //每次初始化就先删除这个小食物。后面再创建
      //外部无法访问的函数
      remove();

      // 创建div
      var div = document.createElement("div");
      //把div加到map中
      map.appendChild(div);
      //设置div的样式
      div.style.width = this.width + "px";
      div.style.height = this.height + "px";
      div.style.backgroundColor = this.color;
      //脱离文档流,以便于在地图上随机显示
      div.style.position = "absolute";
      //随机横纵坐标
      this.x = parseInt(Math.random() * (map.offsetWidth / this.width)) * this.width;
      this.y = parseInt(Math.random() * (map.offsetHeight / this.height)) * this.height;
      div.style.left = this.x + "px";
      div.style.top = this.y + "px";
      //把div加入到数组elements中
      elements.push(div);
    };

    //私有函数----删除食物的。 外部不可以使用
    function remove() {
      //elements数组中有这个食物,我们把这是元素删除
      for (var i = 0; i < elements.length; i++) {
        var ele = elements[i];
        //找到这个子元素的父级元素(map),然后删除这个子元素
        ele.parentNode.removeChild(ele);
        //上面删除的地图中的元素。实质在数组里的元素还没删除,我们需要再次删除elements中的这个子元素
        elements.splice(i, 1);
      }
    }


    //把Food暴露给window,外部可以使用。需要写在方法外面。
    window.Food = Food;
  }());

  //自调用函数——小蛇的
  (function () {
    var elements = [];//存放小蛇的每个身体部分
    //小蛇的构造函数
    function Snake(width, height, direction) {
      //小蛇的每个部分的宽
      this.width = width || 20;
      this.height = height || 20;
      //小蛇的身体
      this.body = [
        {x: 3, y: 2, color: "red"},//头
        {x: 2, y: 2, color: "orange"},//身体
        {x: 1, y: 2, color: "orange"}//身体
      ];
      //方向
      this.direction = direction || "right";
    }

    //为原型添加方法————小蛇的初始化
    Snake.prototype.init = function (map) {
      remove();
      //循环遍历创建div,数组有多少个元素身体就有多大
      for (var i = 0; i < this.body.length; i++) {
        //拿到小蛇身体里的每个对象
        var obj = this.body[i];
        //创建div
        var div = document.createElement("div");
        //把div加入到map地图中
        map.appendChild(div);
        //为每个div设置样式
        div.style.position = "absolute"
        div.style.width = this.width + "px";
        div.style.height = this.height + "px";
        //横纵坐标
        div.style.left = obj.x * this.width + "px";
        div.style.top = obj.y * this.height + "px";
        //小蛇的背景颜色
        div.style.backgroundColor = obj.color;
        //方向
        //把div加入到elements数组中——为了删除。以做成移动的效果
        elements.push(div);
      }
    };

    //为原型添加方法——小蛇动起来
    Snake.prototype.move = function (food, map) {
      //改变小蛇 身体 的位置
      var i = this.body.length - 1; //2 从小蛇身体的第二个位置起
      for (; i > 0; i--) { //倒序
        this.body[i].x = this.body[i - 1].x;  //小蛇身体第1个div的坐标给第2个div(不包含头部)
        this.body[i].y = this.body[i - 1].y;
      }
      //判断方向,改变小蛇的头的坐标位置
      switch (this.direction) {
        case "right":
          this.body[0].x += 1;
          break;
        case "left":
          this.body[0].x -= 1;
          break;
        case "top":
          this.body[0].y -= 1;
          break;
        case "bottom":
          this.body[0].y += 1;
          break;
      }

      //判断小蛇有没有吃到食物(就是判断小蛇蛇头的横纵坐标是否等于食物的坐标)
      //获取小蛇的蛇头坐标
      var headX = this.body[0].x * this.width;
      var headY = this.body[0].y * this.height;
      if (headX == food.x && headY == food.y) {
        //获取小蛇的最后的一个尾巴div
        var last = this.body[this.body.length - 1];
        console.log(last);
        // 把最后的蛇尾复制一个重新加入到小蛇的body当中
        this.body.push({//body里面的元素是一个对象
          x: last.x,
          y: last.y,
          color: last.color
        });
        //把食物删除,重新初始化食物
        food.init(map);
      }
    };

    //删除小蛇的私有的函数
    function remove() {
      var i = elements.length - 1;
      for (; i >= 0; i--) {
        //先从当前的子元素中找到该子元素的父级元素,然后再弄死这个子元素
        var ele = elements[i];
        //从map地图上删除这个子元素div
        ele.parentNode.removeChild(ele);
        elements.splice(i, 1);
      }
    }

    //把Snake构造函数暴露给window,外部可以访问
    window.Snake = Snake;

  }());


  // 自调用函数——游戏对象
  (function () {
    var that = null;//该变量的目的就是为了保存游戏Game的实例对象-------

    //游戏的构造函数
    function Game(map) {
      this.food = new Food(); //食物对象
      this.snake = new Snake(); //小蛇对象
      this.map = map; //地图
      that = this; //保存当前的实例对象到that变量中————此时that就是this
    }

    //初始化游戏————可以设置小蛇和食物显示出来(为原型添加方法)
    Game.prototype.init = function () {
      //食物初始化
      this.food.init(this.map);
      //小蛇初始化
      this.snake.init(this.map);
      //调用自动移动小蛇的方法
      this.runSnake(this.food, this.map);
      //调用按键的方法
      this.bindKey();
    };

    //添加原型方法———设置小蛇可以自动跑起来
    Game.prototype.runSnake = function (food, map) {
      //自动的去移动
      var timeId = setInterval(function () {//setInterval中此时的this是window
        // 移动小蛇
        this.snake.move(food, map);
        //初始化小蛇
        this.snake.init(map);


        //撞墙提示,求出横纵的坐标的最大值
        var maxX = map.offsetWidth / this.snake.width;  //40 地图的宽度/小蛇的宽度
        var maxY = map.offsetHeight / this.snake.height;  //
        //小蛇的头的坐标
        var headX = this.snake.body[0].x;  //蛇头的x轴
        var headY = this.snake.body[0].y;
        //横坐标撞墙
        if (headX < 0 || headX >= maxX) {
          clearInterval(timeId); //清理定时器,停止移动
          alert("游戏结束");
        }
        //纵坐标撞墙
        if (headY < 0 || headY >= maxY) {
          clearInterval(timeId);
          alert("游戏结束");
        }
      }.bind(that), 150)//函数调用了.bind就改变了里面this的指向。从而this指向了实例对象(原本是window)
    };

    //添加原型方法——设置用户按键,改变小蛇移动的方向
    Game.prototype.bindKey = function () {
      //获取文档用户的按键,改变小蛇的方向。
      document.addEventListener("keydown", function (e) {
        //这里的this应该是触发keydown的事件的对象——document,所以这里的this就是document.
        //获取按键的值
        switch (e.keyCode) {
          case 37:
            this.snake.direction = "left";
            break;
          case 38:
            this.snake.direction = "top";
            break;
          case 39:
            this.snake.direction = "right";
            break;
          case 40:
            this.snake.direction = "bottom";
            break;
        }
      }.bind(that), false)
    };

    window.Game = Game;
  }());


  //外部测试代码
  // var ff = new Food();
  // ff.init(document.querySelector(".map"));
  // //ff.init(document.querySelector(".map"));ff.init(document.querySelector(".map")); 多个小食物也只有生成一个
  // var sn = new Snake();
  // sn.init(document.querySelector(".map"))
  //
  var gm = new Game(document.querySelector(".map"));
  gm.init();


</script>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/weixin_44253336/article/details/100239008