贪吃蛇-看谁能赢

跟我一起玩玩儿小游戏吧


html和css框架很简单 为了节约时间就不写在这里啦,直接带大家康康我的js吧~

1、定义游戏中需要的变量

// sw和sh表示蛇和食物的高宽

var sw = 20; 

var sh = 20; 

//tr和td表示行和列 整个盒子是600*600大小的

var tr = 30;

var td = 30;

2、定义由行和列控制的画布

为什么需要定义画布呢?

定义画布能够更加方便的存储蛇和食物的坐标,用来判断蛇头碰到自己和碰到食物以及碰到墙的操作。

比如:

蛇头的大小是20*20的,初始化蛇头的位置是left:40;top:0; 此时存储它的坐标就是[2,0]; 用存储的坐标*盒子的大小就可以得到蛇头的位置。这样就很简单的实现蛇的移动啦

画布里面要实现蛇的初始化 以及处理碰撞后的事件,动态的去存储一些数据,我用到的是 用构造函数去创建对象的方法,这里首先给大家回忆一下构造函数如何创建对象的

语法格式:
     
 function  构造函数名() {
             this.属性 = 值;
             this.方法 = function(sang) {
                   console.log (sang)
                  }

      }
调用构造函数 :
  var  td =     new 构造函数名(); //调用函数返回的是一个对象 也就是对象实例化,这里的this指向的是构造函数的实例对象

贪吃蛇-看谁能赢_第1张图片  

程序实现的大致流程:

  • 当点击开始按钮的时候,就会调用调用game实例对象里面的game.chushihua()方法,动态的创建蛇头和蛇身体以及食物,以及对按键的设置,比如当按下左键的时候 右键是失效的。

我们在蛇的初始化里面要定义一个属性来存储蛇走的一个方向x和y为蛇头下一个位置

this.directionNum = {
        //用对象的方法存储蛇走的方向 设置x 和y是为了计算蛇头下一个位置
        left: {
            x: -1,//表示行走的方向
            y: 0,
            rotate: 180 //蛇头旋转
        },
        right: {
            x: 1,
            y: 0,
            rotate: 0 //蛇头旋转
        },
        up: {
            x: 0,
            y: -1,
            rotate: -90 //蛇头旋转
        },
        down: {
            x: 0,
            y: 1,
            rotate: 90 //蛇头旋转
        }
    };

将蛇和食物都创建好了 就对按键的设置

必须当两个条件都满足的时候,才能把用户按下的方向建给蛇的方向

//用户按键
        document.onkeydown = function (e) {
            //对按键的设置,当用户按下左键的时候,蛇不能往右走
            if (e.which == 37 && snake.direction != snake.directionNum.right) {
                snake.direction = snake.directionNum.left;
            } else if (e.which == 38 && snake.direction != snake.directionNum.down) {
                snake.direction = snake.directionNum.up;
            } else if (e.which == 39 && snake.direction != snake.directionNum.left) {
                snake.direction = snake.directionNum.right;
            } else if (e.which == 40 && snake.direction != snake.directionNum.up) {
                snake.direction = snake.directionNum.down;
            }
        }

 e.which 这里给大家普及一个新的知识点,前面学习了e.keycode 都知道是编码的意思,其实呢which是差不多的

    

运行的结果:当我按下向上的方向键时,输出结果是其ASCll码

贪吃蛇-看谁能赢_第2张图片

二者又有什么区别呢?

  兼容性的问题

Netscape/Firefox/Opera中不支持 window.event.keyCode,需要用event.which代替;
IE用event.keCode方法获取当前被按下的键盘按键值; 

  • 当初始化完后就调用我们的star()方法 

 这里的话肯定是要用到定时器的,每隔200毫秒 就调用一次实例对象snake里面的getNextPos方法,也就是获取蛇头的下一个位置

    this.start = function () {
        //游戏开始
        this.timer = setInterval(function () {
            snake.getNextPos();
        }, 200);
    }

 snake.getNextPos()方法:

 this.getNextPos = function () {
        //这个方法是获取蛇头的下一个位置 
        var np = [this.head.x / sw + this.direction.x, this.head.y / sh + this.direction.y];
        //判断下一个结点是自己 就失败了
        var flag = false;
        this.p.forEach(function (value) {
            if (value[0] == np[0] && value[1] == np[1]) {
                flag = true;
            }
        });
        if (flag) {
            console.log('撞到自己了');
            this.strategies.die.call(this); //call 是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。
            return;
        }
        //下个点是围墙,游戏结束
        if (np[0] < 0 || np[1] < 0 || np[0] > td - 1 || np[1] > tr - 1) {
            console.log('撞到墙啦');
            this.strategies.die.call(this);
            return;
        }
        //下一个是食物。吃
        if (food && food.p[0] == np[0] && food.p[1] == np[1]) {
            //都满足表示蛇头要走的下一个位置是食物的那个点
            console.log('吃到食物啦');
            this.strategies.eat.call(this);
            return;
        }
        //下一个点什么都不是 继续走
        this.strategies.move.call(this);
    };

想必大家很不理解 时如何获取蛇头的下一个位置的

 var np = [this.head.x / sw + this.direction.x, this.head.y / sh + this.direction.y];

this.head是一个对象 里面存储的有蛇头的信息, 从下面的结果可以看到最开始 蛇头的位置

this.head.x = 40

this.head.x / sw = 40/20 =2

如果我们按键按下的是右键 this.direction.x = 1 

此时蛇头的下一个位置就是np = [3,0],对应的left :3*20;top:0*20;

 输出this.head   

紧接着后面就判断是否撞到自己 是否吃到食物 是否碰到墙

 用flag来怕断是否撞到自己 当flag的值为true时就表示撞到自己了,this.p里面就是存储的蛇身体的位置,初始化蛇之后

this.p = [[2,0],[1,0],[0,0]];  用蛇的下一个位置去判断是否和蛇的身体的位置一样,如果位置相等就表示 碰到自己了,游戏就结束

这里又要提到一个重要的知识:forEach语句

注意:forEach不支持在循环中添加删除操作,因为在使用forEach循环的时候,数组(集合)就已经被锁定不能被修改

forEach(function(value,index,array){
    //code something 
  });

forEach方法中的function回调有三个参数:
第一个参数是遍历的数组内容,
第二个参数是对应的数组索引,
第三个参数是数组本身

举例:

运行的结果: 

当蛇头撞到自己的时候不是直接就结束游戏,而是执行this.strategies.die.call(this); 这句话的意思就是改变this的指向,this指向的是strategies方法的里面的die方法

call 是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。这里呢就不细讲两者的区别了,现阶段会用就行啦,

  1. call :调用一个对象的一个方法,用另一个对象替换当前对象
  2. apply:调用一个对象的一个方法,用另一个对象替换当前对象
  • 游戏结束后,清楚计时器,输出得分,游戏回到初始状态
    this.over = function () {
        clearInterval(this.timer);
        alert('你的得分为:' + this.score);
        //游戏再一次回到初识的状态
        var snakeWrap = document.getElementById('snakeWrap');
        snakeWrap.innerHTML = '';
        snake = new Snake();
        game = new Game();
        var startBtnWrap = document.querySelector('.startBtn');
        startBtnWrap.style.display = 'block';
    }

你可能感兴趣的:(css,html,前端,css,html,html5)