跟我一起玩玩儿小游戏吧
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指向的是构造函数的实例对象
程序实现的大致流程:
我们在蛇的初始化里面要定义一个属性来存储蛇走的一个方向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码,
二者又有什么区别呢?
兼容性的问题
Netscape/Firefox/Opera中不支持 window.event.keyCode,需要用event.which代替;
IE用event.keCode方法获取当前被按下的键盘按键值;
这里的话肯定是要用到定时器的,每隔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 的指向。这里呢就不细讲两者的区别了,现阶段会用就行啦,
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';
}