贪吃蛇

最近自学小程序,在练手,就写了个贪吃蛇的代码。借此熟悉下小程序的一些基本元素和语法。

小程序语法上跟js是差不多的。有一些小程序特有的东西。

本篇文章要用到的知识点有:

bindtouchstart属性:绑定触摸开始事件,触摸开始时候触发的,
bindtouchmove属性:绑定触摸移动事件,触摸移动时触发的,
bindtouchend属性:绑定触摸结束事件,触摸结束时触发的,
modal元素:wxapp特殊弹出层元素,
wx:for属性:用来遍历数据的属性,比如等会要遍历的数组ground:wx:for="{{ground}}",
wx:for-item属性:当前遍历对象,
no-cancel属性:是modal元素的属性,写上去表明没有取消按钮,
bindconfirm属性:modal元素的属性,绑定点击确认时触发的函数,
{{}}取值。
这几个比较重要的,一些简单的,就不啰嗦了。
 
界面:
如何开始游戏:触摸屏幕然后移动一段距离,蛇就会开始移动,会根据手指移动的方向来判断蛇的接下来往哪个方向走。
首先先确定一些变量和界面样式,先把界面显示出来。
1、现在pages下面创建snake文件夹,下面包含4个文件(名字都要一样)
wxml就是相当于.html一样的页面文件。wxss就是相当于.css一样的样式文件。
先把页面设计出来:
<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

 

如有不足欢迎指出,欢迎转载~~~

posted on 2018-05-01 00:51  康纳酱  阅读(197)  评论(0编辑  收藏  举报

导航