还是用的vue,本来以为不合适,但想法错了。贪吃蛇并不是通过操作dom来完成移动的,而是通过记录贪吃蛇的路径来将身体渲染出来。
一般移动元素,我们都是变动它的css达到目的,但我在写贪吃蛇的时候发现这样很难以实现,参考了网上的资源,发现大部分人是通过记录贪吃蛇的路径,保存进数组,通过数组变动来表示贪吃蛇的下一步,主要是增加头部位置,去除尾部位置,再动态添加css样式,这样就达到移动的效果。
演示
html&&data:
<div id="snake">
<table>
<tr v-for="(col,y) in cols"><td v-for="(row,x) in rows" :class="body(x,y)||showfood(x,y)?'active':''"></td></tr>
</table>
<button @click="start()">开始</button>
</div>
data(){
return{
rows:'',//横框
cols:'',//竖框
position:[[0,0],[1,0],[2,0],[3,0]],//蛇的初始位置
direction:1,//方向
food:[]//食物的位置
}
},
初始化准备
methods:{
background(){//生成横框和竖框的函数
this.rows=Array(30)
this.cols=Array(30)
},
keyboard(){//键盘事件
let _this = this;
document.onkeydown = function(e){
if(e.keyCode===37){
_this.change(-1)
}else if(e.keyCode===38){
_this.change(-2)
}else if(e.keyCode===39){
_this.change(1)
}else{
_this.change(2)
}
}
},
creatfood(){//创造食物
this.food[0]=Math.floor(Math.random()*30)
this.food[1]=Math.floor(Math.random()*30)
},
showfood(x,y){//显示食物
if(this.food[0]===x&&this.food[1]===y){
return true
}
},
body(x,y){//显示身体
for(i=0;i<this.position.length;i++){//循环身体函数,利用索引和身体位置做毕竟,如果索引和身体数组重合就会添加active样式
if(this.position[i][0]===x&&this.position[i][1]===y){
return true
}
}
},
前期准备就是这么多,接下来就是跑起来,先声明一个计时器
let timer=''
接着就用定时器开始跑
start(){//开始按钮
timer=setInterval(()=>this.autorun(),300)
}
这里的autorun就是我们要写的跑动函数
autorun(){
let direction=this.direction//目前方向
let headX,headY//
headX=this.position[this.position.length-1][0]//复制蛇头的X坐标
headY=this.position[this.position.length-1][1]//复制蛇头的Y坐标
if(direction===1||direction===-1){//如果方向是在左右跑动
direction>0?headX++:headX--//往右跑X坐标+1,往左跑X坐标-1
}else{
direction>0?headY++:headY--//如果方向是在上下跑动,Y坐标做对应处理
}
//此时蛇头的下一个坐标位置就是[headX,headY],接下来就可以判断是否结束游戏,如果结束了,蛇头就没必要添加了
if(headX<0||headX>29||headY<0||headY>29||this.body(headX,headY)){//当蛇头下一个位置出了边界或者这个位置是符合身体函数(即蛇头撞上了身体)
alert('Game Over')//结束
clearInterval(timer)//清除定时器
this.position=[[0,0],[1,0],[2,0],[3,0]]//还原身体
this.creatfood()//重新创造食物
this.direction=1//还原方向
}else{//如果蛇头下一个位置是符合规则的
this.position.push([headX,headY])//将下一个位置添加进数组,头部长一节
if(headX!==this.food[0]||headY!==this.food[1]){//如果下一个头部位置不是食物的位置,即吃食物开始
this.position.shift()//我们将尾部去掉,一长一短实现了蛇的走动
}else{//如果下一个头部位置是食物
this.creatfood()//不去除尾部,再次创建食物(这里有个小bug,随机的食物有几率与身体重合)
}
}
},
change(dir){//改变方向
if(Math.abs(dir)===Math.abs(this.direction)){//如果方向相同或者想法,不做任何操作
return
}else{
this.direction=dir//否则把方向改动
}
},
就是这个样子,贪吃蛇就写完了,逻辑方面并不是太复杂,但是对于数组的操作有很多,这里提下我碰见的几个问题:
- vue中的数组对象在更新的时候和单独的数据是不一样的,只有某些特定的函数会去主动更新数组,否则的话需要用vm.$set( target, key, value )这个去更新数组
- 二维数组和一维数组是不同的,indexOf()不管用,无法检测;而且类似this.position[0]===[0,0]也是无法正确判断的,这导致我在写这个小demo的时候犯了很多错。
let po=[[0,0],[1,0],[2,0],[3,0]]
let qo=[[0,0]]
let oo=[0,0]
console.log(po[0]==qo[0])//false
console.log(qo[0]==oo)//false
这大概就是最大的收获,我还太年轻。
因为JavaScript里面Array是对象,==或===操作符只能比较两个对象是否是同一个实例,也就是是否是同一个对象引用。目前JavaScript没有内置的操作符判断对象的内容是否相同。
但是惯性思维让人以为数组也是值,是可以比较的。(这段是复制的,别人总结的,和我想法一模一样)希望能给同为小白的朋友们提个醒
源码
https://github.com/yuyeqianxu…
希望能帮助到和我一样的小白朋友们,有bug麻烦反馈,谢谢!