美文网首页
JavaScript手写贪吃蛇小游戏

JavaScript手写贪吃蛇小游戏

作者: 又菜又爱分享的小肖 | 来源:发表于2021-07-24 10:51 被阅读0次

    大家小时候都玩过诺基亚里面的贪吃蛇吧,今天也来自己写个贪吃蛇小游戏。

    1. 首先呢。需要创建自己的地图,一个宽为800px,高为600px,定位在浏览器的左上角。
    <!DOCTYPE html>
    <html>
    <head lang="en">
      <meta charset="UTF-8">
      <title>贪吃蛇</title>
      <style>
        #map {
          width: 800px;
          height: 600px;
          background-color: #ccc;
          position: relative;
          top: 0px;
          left: 0px;
        }
      </style>
    </head>
    <body>
    <div id="map"></div>
    </body>
    </html>
    
    1. 我们现在需要创建一个食物,我们发现贪吃蛇里面的每一个食物都有宽,高,背景色,位置(x,y)坐标轴,用编程中面向对象的思维,可以视为食物就是一个对象,那就需要一个创建食物对象的构造函数。
        // 如果Food函数未传值,那就默认为宽为20px,高为20px,背景色为绿色,(x,y)坐标都为0就是原点。
        function Food(width = 20, height = 20, bgc = 'green', x = 0, y = 0) {
            this.width = width;
            this.height = height;
            this.bgc = bgc;
            this.x = x;
            this.y = y;
        }
    
    1. 那如何把这个食物对象显示在地图上呢?这时候需要一个显示食物对象的一个函数。
        Food.prototype.render = function (map) {
            //给食物对象随机坐标x, y
            //offsetWidth = border + padding + height;
            //Math.random()方法返回一个大于等于0小于1的随机数
            //Math.floor()方法执行向下取整
            this.x = Math.floor(Math.random() * map.offsetWidth / this.width) * this.width;
            this.y = Math.floor(Math.random() * map.offsetHeight / this.height) * this.height;
            //创建一个div,让这个div拥有这个食物对象的所有信息 (把食物对象的xy宽高背景色都赋值给这个div)
            let box = document.createElement('div');
            box.style.position = "absolute";
            box.style.left = this.x + 'px';
            box.style.top = this.y + 'px';
            box.style.backgroundColor = this.bgc;
            box.style.width = this.width + 'px';
            box.style.height = this.height + 'px';
            //把这个div添加到map地图中
            map.appendChild(box);
        }
    

    4.然后在这个构造函数暴露出去。

    window.Food = Food;
    
    1. 导入到html文件中,实例化Food构造函数
    let food = new Food();
    food.render(map);
    

    这个时候我们的随机食物就已经做出来了,接下来去写蛇这个对象

    1. 蛇有什么特点呢?蛇和食物一样是有宽,高,背景色,还会移动,移动的方向,(x,y)坐标轴,蛇也是一个对象
        function Snake(width = 20, height = 20, direction = 'right') {
            this.width = width;
            this.height = height;
            this.direction = direction;
            //这个body就是描述蛇的每一节的
            this.body = [
                { x: 3, y: 1, bgc: 'red' },
                { x: 2, y: 1, bgc: 'pink' },
                { x: 1, y: 1, bgc: 'green' }
            ]
        }
    

    2.把创建出来的蛇显示在地图上

        Snake.prototype.render = function (map) {
            //遍历蛇一节一节的显示
            for (let i = 0; i < this.body.length; i++) {
                let item = this.body[i];
                let box = document.createElement('div');
                box.style.position = 'absolute';
                box.style.left = item.x * this.width + 'px';
                box.style.top = item.y * this.height + 'px';
                box.style.width = this.width + 'px';
                box.style.height = this.height + 'px';
                box.style.backgroundColor = item.bgc;
                //把box添加到map中
                map.appendChild(box);
            }
        }
    

    3.让蛇动起来吃东西

        Snake.prototype.move = function (food, map) {
            //除了蛇头之外的蛇身边的坐标改变
            for (var i = this.body.length - 1; i > 0; i--) {
                this.body[i].x = this.body[i - 1].x;
                this.body[i].y = this.body[i - 1].y;
            }
            //蛇头的坐标改变
            switch (this.direction) {
                case 'right':
                    this.body[0].x++;
                    break;
                case 'left':
                    this.body[0].x--;
                    break;
                case 'top':
                    this.body[0].y--;
                    break;
                case 'bottom':
                    this.body[0].y++;
                    break;
            }
            //判断蛇是否吃到了食物.
            //如果蛇头的坐标与食物的坐标重叠,那就说明蛇已经吃到了食物
            var snakeHeadX = this.body[0].x * this.width; //蛇头的x坐标.
            var snakeHeadY = this.body[0].y * this.height; //蛇头的y坐标
            var foodX = food.x;//食物的x坐标
            var foodY = food.y;//食物的y坐标.
            //先拿到蛇尾巴(方便取他的xy)
            var lastSnakeUnit = this.body[this.body.length - 1];
            //判断
            if (snakeHeadX == foodX && snakeHeadY == foodY) {
                //吃到了食物就要长身体.
                this.body.push({
                    x: lastSnakeUnit.x,
                    y: lastSnakeUnit.y,
                    bgColor: getRandomColor()
                });
                //吃了食物要产生一个新的食物.
                food.render(map);
            }
        }
    

    4.蛇每次吃到一个食物,那么蛇就会加一个box,这个box是随机颜色,我们需要写一个随机颜色的函数

        //随机生成16进制的颜色函数
        function getRandomColor() {
            let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; //下标0-15
            let str = '#';
            //循环产生 6个 0-15的数.
            for (let i = 0; i < 6; i++) {
                let num = Math.floor(Math.random() * 16);
                //根据这个随机数,在arr数组中去取值.
                str += num;
            }
            return str;
        }
    

    5.把这个蛇的构造函数暴露出去

    window.Snake = Snake;
    
    1. 这个时候食物和蛇都有在地图上显示了,但是蛇还不能动,现在我们需要写一个让蛇不停的动起来的方法,本质就是用计时器不停的调用蛇的move和render方法。
        function snakeAutoMove() {
            var timerId = setInterval(function () {
                //判断蛇移动后有没有出界.
                //思路:蛇头的坐标小于0,或者大于宽高就出界.
                var snakeHeadX = this.snake.body[0].x * this.snake.width;//蛇头x坐标
                var snakeHeadY = this.snake.body[0].y * this.snake.height;//蛇头y坐标
                if (snakeHeadX < 0 || snakeHeadY < 0 || snakeHeadX >= this.map.offsetWidth || snakeHeadY >= this.map.offsetHeight) {
                    alert('Game Over!');
                    clearInterval(timerId);
                    return;//判断蛇出界了,那就提前结束这个方法,就不要让他往下执行这个渲染.
                }
                this.snake.render(this.map);
            }.bind(that), 400);
        }
    
    1. 现在蛇能动了,现在需要控制蛇的运动方向,让蛇根据键盘按键来移动。
        function bindKey() {
            document.onkeydown = function (e) {
                e = e || window.event;
                //console.log(e.keyCode);//左37  上38 右39  下40
                switch (e.keyCode) {
                    case 37:
                        if (this.snake.direction != 'right') {
                            this.snake.direction = 'left';
                        }
                        break;
                    case 38:
                        if (this.snake.direction != 'bottom') {
                            this.snake.direction = 'top';
                        }
                        break;
                    case 39:
                        if (this.snake.direction != 'left') {
                            this.snake.direction = 'right';
                        }
                        break;
                    case 40:
                        if (this.snake.direction != 'top') {
                            this.snake.direction = 'bottom';
                        }
                        break;
                }
            }.bind(that);
        }
    

    完整代码

    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>贪吃蛇</title>
    </head>
    <script src="index.js"></script>
    <body>
        <div id="map"></div>
    </body>
    
    </html>
    <style>
        #map {
            position: relative;
            left: 0;
            top: 0;
            width: 800px;
            height: 600px;
            background-color: #ccc;
        }
    </style>
    <script>
    //获取地图对象
    let map = document.querySelector("#map");
      //实例化一个游戏控制器对象.
      var game1 = new Game(map);
      //调用游戏控制器的start方法让游戏开始.
      game1.start();
    </script>
    

    index.js

    ;(function (window) {
      //声明一个数组,用来保存食物
      var list = [];
      //1.经过分析,发现食物有宽/高/背景色/定位的xy坐标,所以食物是一个对象,那就有一个创建食物对象的构造函数.
      function Food(width,height,bgColor,x,y){
        this.width = width || 20;
        this.height = height || 20;
        this.bgColor = bgColor || "green";
        this.x = x || 0;
        this.y = y || 0;
      }
    
      Food.prototype.render = function (map) {
        //渲染新食物之前删除老食物div.
        remove(map);
    
        //2.1 给食物对象随机产生xy坐标
        this.x = Math.floor(Math.random()*map.offsetWidth/this.width) * this.width;
        this.y = Math.floor(Math.random()*map.offsetHeight/this.height)*this.height;
        //2.2 创建一个div,让这个div拥有这个食物对象的所有显示信息.(把食物对象的xy宽高背景色都赋值给div的样式)
        var div1 = document.createElement('div');
        div1.style.position = 'absolute';
        div1.style.left = this.x + "px";
        div1.style.top = this.y + 'px';
        div1.style.width = this.width + "px";
        div1.style.height = this.height + 'px';
        div1.style.backgroundColor = this.bgColor;
        //2.3 把这个div添加到map地图中.
        map.appendChild(div1);
    
        //2.4 把渲染食物的这个div用list数组存起来
        list.push(div1);
      }
    
      //删除老食物div的方法.
      function remove(map){
        for(var i = 0 ; i < list.length; i++){
          map.removeChild(list[i]);
        }
        list = []; //清空list
      }
    
    
    
      //3.把Food这个构造函数给暴露出去.
      window.Food = Food;
    
    }(window));
    
    
    //----------------------------------------------------------------------------------
    ;(function (window) {
      //随机产生一个十六进制的颜色的函数.
      function getColorForRandom(){
        var arr = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];  //下标0-15
        var str = "#";
        //循环产生 6个 0-15的数.
        for(var i = 0 ; i < 6; i++){
          var num = Math.floor(Math.random()*16);
          //根据这个随机数,在arr数组中去取值.
          str += arr[num];
        }
        return str;   //"#98de00"
      }
    
    
    
    
      //写蛇
      //声明一个数组list用来保存蛇.
      var list = [];
    
      //1.分析蛇也是有宽/高/背景色/移动的方向/定位xy坐标,蛇也是对象.
      function Snake(width,height,direction){
        this.width = width || 20;
        this.height = height || 20;
        this.direction = direction || 'right';
        //这个body就是描述蛇的每一节的.
        this.body = [
          {x:3,y:1,bgColor:'red'},
          {x:2,y:1,bgColor:'green'},
          {x:1,y:1,bgColor:'pink'}
        ];
      }
    
    
      //2.把创建出来的蛇对象显示在map地图上.
      Snake.prototype.render = function (map) {
        //渲染新蛇的时候删除老蛇(调用remove方法)
        remove(map);
    
        //2.1 遍历蛇一节一节的显示.
        for(var i = 0 ; i < this.body.length; i++){
          //2.2 创建div
          var div1 = document.createElement('div');
          //2.3 让这个div拥有这个蛇节的每一个显示信息
          div1.style.position = 'absolute';
          div1.style.top = this.body[i].y * this.height + 'px';
          div1.style.left = this.body[i].x * this.width + 'px';
          div1.style.width = this.width + 'px';
          div1.style.height = this.height + 'px';
          div1.style.backgroundColor = this.body[i].bgColor;
          //2.4 把div1添加到map中.
          map.appendChild(div1);
    
          //把渲染蛇用的div存进list数组中.
          list.push(div1);
        }
      }
    
    
      //4.删除老蛇的方法.
      function remove(map){
        //从map中移除老蛇div.
        for(var i = 0 ; i < list.length; i++){
          map.removeChild(list[i]);
        }
        //list数组清空.
        list.length = 0; // list = [];
      }
    
    
      //3.让蛇移动的方法写在蛇的原型中.
      Snake.prototype.move = function (food,map) {
        //除了蛇头之外的蛇身体的坐标改变
        for(var i = this.body.length-1; i>0 ;i--){
          this.body[i].x = this.body[i-1].x;
          this.body[i].y = this.body[i-1].y;
        }
    
        //蛇头的坐标改变
        switch (this.direction){
          case 'right':
            this.body[0].x++;
            break;
          case 'left':
            this.body[0].x--;
            break;
          case 'top':
            this.body[0].y--;
            break;
          case 'bottom':
            this.body[0].y++;
            break;
        }
    
        //判断蛇是否吃到了食物.
        //思路:蛇头的坐标和食物的坐标重合.
        var snakeHeadX = this.body[0].x * this.width; //蛇头的x坐标.
        var snakeHeadY = this.body[0].y * this.height; //蛇头的y坐标
        var foodX = food.x;//食物的x坐标
        var foodY = food.y;//食物的y坐标.
        //先拿到蛇尾巴(方便取他的xy)
        var lastSnakeUnit = this.body[this.body.length-1];
        //判断
        if(snakeHeadX == foodX && snakeHeadY == foodY){
          //吃到了食物就要长身体.
          this.body.push({
            x:lastSnakeUnit.x,
            y:lastSnakeUnit.y,
            bgColor:getColorForRandom()
          });
          //吃了食物要产生一个新的食物.
          food.render(map);
        }
    
    
      }
    
    
    
      //3.把Snake这个构造函数给暴露出去
      window.Snake = Snake;
    
    }(window));
    
    
    //------------------------------------------------------------------------
    ;(function (window) {
      //声明一个变量that,一开始赋值null
      var that = null;
    
      //1.创建游戏控制器对象的构造函数.
      function Game(map){
        //游戏控制器对象,里面有食物对象,食物对象是调用食物的构造函数创建出来的.
        this.food = new Food();
        //游戏控制器对象,里面有蛇对象,蛇对象是调用蛇的构造函数创建出来的.
        this.snake = new Snake();
        //游戏控制对象,里面有地图,地图是传进来的.
        this.map = map;
    
        //给that赋值.
        that = this;
      }
    
      //2.游戏开始的方法.
      Game.prototype.start = function () {
        //2.1 显示食物对象.
        this.food.render(this.map);
        //2.2 显示蛇对象
        this.snake.render(this.map);
    
        //2.3 让蛇动一下,调用蛇对象的move方法改坐标,调用蛇的render方法重新渲染.
        //this.snake.move();
        //this.snake.render(this.map);
        snakeAutoMove();
    
        //2.4 调用绑定按键方法.
        bindKey();
      }
    
    
      //3.写一个方法让蛇不停的动起来.本质就是用一个计时器不停的调用蛇的move和render方法.
      function snakeAutoMove(){
        var timerId = setInterval(function () {
          //需求:把此时这个this变成游戏控制器对象
          this.snake.move(this.food,this.map);
    
          //判断蛇移动后有没有出界.
          //思路:蛇头的坐标小于0,或者大于宽高就出界.
          var snakeHeadX = this.snake.body[0].x *this.snake.width;//蛇头x坐标
          var snakeHeadY = this.snake.body[0].y *this.snake.height;//蛇头y坐标
          if(snakeHeadX < 0 || snakeHeadY < 0 || snakeHeadX >= this.map.offsetWidth || snakeHeadY >= this.map.offsetHeight){
            alert('Game Over!');
            clearInterval(timerId);
            return;//判断蛇出界了,那就提前结束这个方法,就不要让他往下执行这个渲染.
          }
    
          this.snake.render(this.map);
    
    
        }.bind(that),400);
      }
    
      //4.绑定键盘按键方法,让蛇根据键盘按键来移动.
      function bindKey(){
        document.onkeydown = function (e) {
          e = e || window.event;
          //console.log(e.keyCode);//左37  上38 右39  下40
          switch(e.keyCode){
            case 37:
              if(this.snake.direction != 'right'){
                this.snake.direction = 'left';
              }
              break;
            case 38:
              if(this.snake.direction != 'bottom'){
                this.snake.direction = 'top';
              }
              break;
            case 39:
              if(this.snake.direction != 'left'){
                this.snake.direction = 'right';
              }
              break;
            case 40:
              if(this.snake.direction != 'top'){
                this.snake.direction = 'bottom';
              }
              break;
          }
        }.bind(that);
      }
    
    
      //3.把Game构造函数给暴露出去.
      window.Game = Game;
    
    }(window));
    

    相关文章

      网友评论

          本文标题:JavaScript手写贪吃蛇小游戏

          本文链接:https://www.haomeiwen.com/subject/dznymltx.html