贪吃蛇
最近自学小程序,在练手,就写了个贪吃蛇的代码。借此熟悉下小程序的一些基本元素和语法。
小程序语法上跟js是差不多的。有一些小程序特有的东西。
本篇文章要用到的知识点有:


<view class="scoreBar">
<view class="snakeView">Snake</view>
<view class="scoreView">
<view class="text">得分</view>
<view class="num">{{score}}</view>
</view>
<view class="scoreView">
<view class="text">最高</view>
<view class="num">{{maxscore}}</view>
</view>
</view>
<!-- controller要把ground包起来 -->
<view class="controller" bindtouchstart='tapStart' bindtouchmove='tapMove' bindtouchend='tapEnd'>
<view class="ground">
<view wx:for="{{ground}}" class="row" wx:for-item="row">
<view wx:for="{{row}}" class="block block_{{item}}">
</view>
</view>
</view>
</view>
<!--no-cancel 隐藏cancel按钮 -->
<modal class="modal" hidden="{{modalHidden}}" no-cancel bindconfirm="modalChange">
<view>游戏结束,重新开始吗?</view>
</modal>
然后是wxss:
.controller{
width: 100%;
height: 100%;
}
.scoreBar{
display: flex;
height: 85px;
padding: 3px
}
.scoreBar view{
flex: 1;
margin: 3px 3px;
border: 1px grey solid;
background-color: grey;
}
.snakeView{
line-height: 70px;
text-align: center;
font-size: 21px;
background-color: deepskyblue !important;
border-color: deepskyblue !important;
}
.text{
text-align: center;
font-size: 12px;
height:40% !important;
line-height: 30px;
}
.num{
text-align: center;
height:40% !important;
margin-right: 5px;
line-height: 30px;
}
.ground{
width: 660rpx;
height:840rpx;
margin-left: 40rpx;
/* background-color: #ccc; */
}
.row{
width:660rpx;
height: 30rpx;
}
.block{
width:30rpx;
height: 30rpx;
float: left;
background: grey
}
.block_1{
background:#f00;
}
.block_2{
background:#0f0;
}
接着会发现wxss里面有一些元素是要取值的(这种{{}}就是取值表达式),是从.js文件里面定义的Page对象里面data属性来取。
所以接下来写js里面的名为data的json对象:
var app=getApp();
Page({
data:{
//隐藏modal
modalHidden:true,
//分数
score:0,
maxscore:0,
//场地(二维数组,存里面颜色的值,0是灰色,1是红色,2是绿色)
ground: [],
rows:28,
cols:22,
//蛇(存的是坐标,例如[[4,5],[5,5],[6,5]],后面会用这个坐标具体修改场地里面的颜色)
snake:[],
food:[],
//方向
direction:'',
//起始位置
startx:0,
starty:0,
//结束位置
endx:0,
endy:0,
//时间
timer:'',
//判断横竖的变量,大于49是竖
probability : Math.floor(Math.random() * 100)
}
});
getApp()是用来获取小程序实例。
关于Page()函数详细请看:https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/page.html
本篇贪吃蛇只用到了Page的data和onLoad。
data就是页面的初始数据,onLoad是页面加载的回调函数,一个页面只会调用一次。
然后是onLoad函数:
onLoad:function(){
var maxscore = wx.getStorageSync("maxscore")
if (!maxscore){
maxscore = 0;
}
this.setData({ maxscore: maxscore });
this.initGround(this.data.rows, this.data.cols);
//初始化蛇
this.initSnake(3);
//初始化食物
this.createFood();
//移动
this.move();
},
初始化场地、蛇、食物:
initGround:function(rows,cols){
for(var i=0;i<rows;i++){
var colArr=[]
this.data.ground.push(colArr);
for(var j=0;j<cols;j++){
this.data.ground[i].push(0)
}
}
},
initSnake: function (len){
var x = Math.floor(Math.random() * this.data.rows);
var y = Math.floor(Math.random() * this.data.cols);
if (this.data.probability>49){
//heng
//if排除越界情况
if (y > this.data.cols - len) {
//重新生成
this.initSnake(len)
} else {
for (var i = 0; i < len; i++) {
this.data.ground[x][y + i] = 1;
this.data.snake.push([x, y + i]);
}
}
}else{
//shu
//if排除越界情况
if (x > this.data.rows-len){
//重新生成
this.initSnake(len)
}else{
for (var i = 0; i < len; i++) {
this.data.ground[x + i][y] = 1;
this.data.snake.push([x + i, y]);
}
}
}
},
createFood:function(){
var x=Math.floor(Math.random()*this.data.rows);
var y=Math.floor(Math.random()*this.data.cols);
var ground=this.data.ground;
//不能生成在蛇身体的位置
if (ground[x][y]==1){
this.createFood()
}else{
ground[x][y] = 2
this.setData({
ground: ground,
food: [x, y]
})
}
},
这样页面还差一步就能显示了,就是修改utils下面的app.json内容将snake进行加载,把红色框里面的写在第一行就能显示出页面了

接下来是触摸事件(游戏开始和控制方向):
//触发游戏:
tapStart:function(event){
this.setData({
startx:event.touches[0].pageX,
starty:event.touches[0].pageY
})
},
tapMove:function(event){
this.setData({
endx:event.touches[0].pageX,
endy:event.touches[0].pageY
})
},
tapEnd:function(event){
//判断触摸结果,获取横竖方向的触摸移动距离
var heng=(this.data.endx)?(this.data.endx-this.data.startx):0;
var shu=(this.data.endy)?(this.data.endy-this.data.starty):0;
var direction='';
if(Math.abs(heng)>5 || Math.abs(shu)>5){
//根据横竖判断方向(0:上下,1:左右)
direction=(Math.abs(heng)>Math.abs(shu))?this.computeDir(1,heng):this.computeDir(0,shu);
switch(direction){
case 'left':
if(this.data.direction=='right') return;
break;
case 'right':
if(this.data.direction=='left') return;
break;
case 'top':
if(this.data.direction=='bottom') return;
break;
case 'bottom':
if(this.data.direction=='top') return;
break;
default:;
}
}
this.setData({
startx:0,
starty:0,
endx:0,
endy:0,
direction: direction
})
},
computeDir:function(heng,num){
if(heng){
return (num>0)?'right':'left';
}
return (num>0)?'bottom':'top';
},
关于触摸事件的对象,他们的结构,我这里将其copy了出来:
{
"type": "touchstart",
"timeStamp": 2439,
"target": {
"id": "",
"offsetLeft": 221,
"offsetTop": 199,
"dataset": {}
},
"currentTarget": {
"id": "",
"offsetLeft": 0,
"offsetTop": 91,
"dataset": {}
},
"touches": [
{
"identifier": 0,
"pageX": 221,
"pageY": 200,
"clientX": 221,
"clientY": 200
}
],
"changedTouches": [
{
"identifier": 0,
"pageX": 221,
"pageY": 200,
"clientX": 221,
"clientY": 200
}
]
}
{
"type": "touchmove",
"timeStamp": 2689,
"target": {
"id": "",
"offsetLeft": 161,
"offsetTop": 223,
"dataset": {}
},
"currentTarget": {
"id": "",
"offsetLeft": 0,
"offsetTop": 91,
"dataset": {}
},
"touches": [
{
"identifier": 0,
"pageX": 165,
"pageY": 365,
"clientX": 165,
"clientY": 365
}
],
"changedTouches": [
{
"identifier": 0,
"pageX": 165,
"pageY": 365,
"clientX": 165,
"clientY": 365
}
]
}
{
"type": "touchend",
"timeStamp": 18564,
"target": {
"id": "",
"offsetLeft": 101,
"offsetTop": 259,
"dataset": {}
},
"currentTarget": {
"id": "",
"offsetLeft": 0,
"offsetTop": 91,
"dataset": {}
},
"touches": [],
"changedTouches": [
{
"identifier": 0,
"pageX": 188,
"pageY": 284,
"clientX": 188,
"clientY": 284
}
]
}
然后是蛇的移动:
以左拐弯为例,假如一个竖直的蛇,头向下,并且要左拐弯,那么snake数组里面snake[0]尾巴的坐标都是要去掉的(但是也要单独保存),然后后面的元素往前面copy,最后更新最新蛇头的坐标,逻辑就是这样了。

然后就是将功能用代码实现(主要看left,因为剩下3个方向其实是差不多的):
move: function () {
var that = this;
this.data.timer = setInterval(function () {
that.changeDirection(that.data.direction);
that.setData({
ground: that.data.ground
})
}, 400);
},
changeDirection:function(dir){
switch(dir){
case 'left': return this.changeLeft();break;
case 'right': return this.changeRight();break;
case 'top': return this.changeTop();break;
case 'bottom': return this.changeBottom();break;
default:;
}
},
changeLeft:function(){
var snakeArr = this.data.snake;
var snakeLen = this.data.snake.length;
//蛇尾坐标
var snakeTail = snakeArr[0]
var ground = this.data.ground;
ground[snakeTail[0]][snakeTail[1]] = 0;//尾巴位置重置颜色
for (var i = 0; i < snakeLen - 1; i++) {//数组元素往前移一位,尾巴剔除
snakeArr[i] = snakeArr[i + 1];
}
//修改头的坐标
var x = snakeArr[snakeLen-1][0];
var y = snakeArr[snakeLen - 1][1] - 1;//往左
snakeArr[snakeLen-1]=[x,y]
//蛇头坐标(push操作是栈的先进后出)
var snakeHead = snakeArr[snakeLen - 1]
this.checkGame(snakeTail, snakeArr, snakeLen, snakeHead)//检查游戏是否结束
for(var i=0;i<snakeLen;i++){
ground[snakeArr[i][0]][snakeArr[i][1]] = 1
}
this.setData({
ground:ground,
snake: snakeArr
})
},
changeRight: function () {
var snakeArr = this.data.snake;
var snakeLen = this.data.snake.length;
//蛇尾坐标
var snakeTail = snakeArr[0]
var ground = this.data.ground;
ground[snakeTail[0]][snakeTail[1]] = 0;//尾巴位置重置颜色
for (var i = 0; i < snakeLen - 1; i++) {//数组元素往前移一位,尾巴剔除
snakeArr[i] = snakeArr[i + 1];
}
//修改头的坐标
var x = snakeArr[snakeLen - 1][0];
var y = snakeArr[snakeLen - 1][1] + 1;//往右
snakeArr[snakeLen - 1] = [x, y]
//蛇头坐标(push操作是栈的先进后出)
var snakeHead = snakeArr[snakeLen - 1]
this.checkGame(snakeTail, snakeArr, snakeLen, snakeHead)//检查游戏是否结束
for (var i = 0; i < snakeLen ; i++) {
ground[snakeArr[i][0]][snakeArr[i][1]] = 1
}
this.setData({
ground: ground,
snake: snakeArr
})
},
changeTop: function () {
var snakeArr = this.data.snake;
var snakeLen = this.data.snake.length;
//蛇尾坐标
var snakeTail = snakeArr[0]
var ground = this.data.ground;
ground[snakeTail[0]][snakeTail[1]] = 0;//尾巴位置重置颜色
for (var i = 0; i < snakeLen - 1; i++) {//数组元素往前移一位,尾巴剔除
snakeArr[i] = snakeArr[i + 1];
}
//修改头的坐标
var x = snakeArr[snakeLen - 1][0] - 1;//往上
var y = snakeArr[snakeLen - 1][1];
snakeArr[snakeLen - 1] = [x, y]
//蛇头坐标(push操作是栈的先进后出)
var snakeHead = snakeArr[snakeLen - 1]
this.checkGame(snakeTail, snakeArr, snakeLen, snakeHead)//检查游戏是否结束
for (var i = 0; i < snakeLen; i++) {
ground[snakeArr[i][0]][snakeArr[i][1]] = 1
}
this.setData({
ground: ground,
snake: snakeArr
})
},
changeBottom: function () {
var snakeArr = this.data.snake;
var snakeLen = this.data.snake.length;
//当前蛇尾坐标
var snakeTail = snakeArr[0]
var ground = this.data.ground;
ground[snakeTail[0]][snakeTail[1]] = 0;//尾巴位置重置颜色
for (var i = 0; i < snakeLen - 1; i++) {//数组元素往前移一位,尾巴剔除
snakeArr[i] = snakeArr[i + 1];
}
//更新当前蛇头的坐标
var x = snakeArr[snakeLen - 1][0] + 1;//往下
var y = snakeArr[snakeLen - 1][1];
snakeArr[snakeLen - 1] = [x, y]
//蛇头坐标(push操作是栈的先进后出)
var snakeHead = snakeArr[snakeLen - 1]
//检查游戏是否结束,判断是否吃到食物,如果吃到,尾巴变长
this.checkGame(snakeTail, snakeArr, snakeLen, snakeHead)
for (var i = 0; i < snakeLen; i++) {
ground[snakeArr[i][0]][snakeArr[i][1]] = 1
}
this.setData({
ground: ground,
snake: snakeArr
})
},
checkGame: function (snakeTail, snake, len, snakeHead){
//碰到边界
if (snakeHead[0] < 0 || snakeHead[0]>=this.data.rows || snakeHead[1]>=this.data.cols || snakeHead[1] < 0){
clearInterval(this.data.timer);
this.setData({
modalHidden:false
})
}
//头碰到了身体其他部位
for(var i=0;i<len-1;i++){
if (snake[i][0] == snakeHead[0] && snake[i][1]==snakeHead[1]){
clearInterval(this.data.timer);
this.setData({
modalHidden: false
})
}
}
//吃食物
if (snakeHead[0] == this.data.food[0] && snakeHead[1] == this.data.food[1]) {//吃到食物
snake.unshift(snakeTail);
this.setData({
score: this.data.score + 10
});
this.storeScore();
this.createFood();
}
},
//计分器(刷新最高分)
storeScore: function () {
if (this.data.maxscore < this.data.score) {
this.setData({
maxscore: this.data.score
})
wx.setStorageSync("maxscore", this.data.maxscore)
}
},
modalChange:function(){
this.setData({
scoure:0,
ground:[],
snake:[],
food:[],
modalHidden:true,
direction:''
})
this.onLoad()
}
源码地址:https://download.csdn.net/download/u010591472/10310123
如有不足欢迎指出,欢迎转载~~~
浙公网安备 33010602011771号