一、新建一个quick start项目看看结构
 
在微信开发工具点击添加项目,选择 无appid,勾上"在当前目录中创建quick start 项目"。 
 
  
 
  
 
 可以看到一共有两个目录 pages和utils,和根目录下的3个app文件。pages存放的是小程序的页面,每个也面都有自己独立的文件夹。 一个页面由4文件构成,js文件是程序逻辑;wxss是微信定义的样式文件,语法跟css一样,支持的样式要少一些;wxml文件用来定义小程序的界面,作用类似于html,但是只能用微信自定义的一些标签,而且页面元素不能直接用js操作,只能通过绑定数据来修改;json是页面的配置文件一般用不着。根目录下的app.js,app.json,app.wxml作用和pages下面的作用类似,只不过pages下面的是页面级的,根目录下的是成个应用级的>。utils下面定义了一个转换时间格式工具函数,然后通过module.exports将函数暴露出去,再在logs.js中通过require引入。
 
  
 
 
  
 
  
 
二、改造index页面
 
 知道了小程序的结构就可以动手开始做了,首先把index页面改造一下,把用户头像上的点击事件去掉,然后再新增两个按钮,用来跳转到游戏主界面和游戏成绩界面。
 
 1.界面,bindtap相当于html的onclick
 
 <!--index.wxml-->
<view class="container">
  <view  class="userinfo">
  <text class="userinfo-nickname"></text>
    <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image>
    <text class="userinfo-nickname">{{userInfo.nickName}}</text>
    <text class="userinfo-nickname">你好</text>
  </view>
  <view class="usermotto"  >
    <text class="user-motto" bindtap="startGame">{{motto}}</text>
  </view>
  <view style="margin-top:30rpx; ">
    <text class="user-motto"  bindtap="viewScore" >查看排名</text>
  </view>
</view>
2.index.js 文件的Page中增加两个处理点击事件的函数,用wx.navigateTo来跳转的目标页
 
 //index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    motto: '开始游戏',
    userInfo: {},
    welcome:'你好'
  },
  //事件处理函数
  startGame: function() {
    wx.navigateTo({
      url: '../game/game'
    })
  },viewScore: function() {
    wx.navigateTo({
      url: '../logs/logs'
    })
  },
  onLoad: function () {
    console.log('onLoad')
    var that = this
    //调用应用实例的方法获取全局数据
    app.getUserInfo(function(userInfo){
      //更新数据
      that.setData({
        userInfo:userInfo
      })
    })
  }
})
 3.index.wxss中增加游戏背景图
 page{background: url('../images/gamebg.jpg') center top; }
 在pages下新建一个images目录存照片
 图片下载地址
  http://pan.baidu.com/share/link?shareid=1434710849&uk=3758963053
  
 
 
  
 
  
 
三、游戏主界面和逻辑
 
 在pages目录下新建一个game目录,然后再照着index页面新建game.js、game.json、game.wxml、game.wxss4个文件。
 然后在app.json中增加game页的配置,顺便改下游戏标题.
 
 
 {
  "pages":[
    "pages/index/index",
    "pages/logs/logs",
    "pages/game/game" 
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor":"white",
    "navigationBarTitleText": "微信翻牌小程序",
    "navigationBarTextStyle":"black"
  }
}
 
 1.game.js 游戏的逻辑
 
 游戏的界面元素不能直接用js操作,只能用page的setData方法通过重新绑定数据来修改.界面上要用的变量都要定义在page的data中,界面有变化时必须要通过this.setData方法修改变量的值,直接修改变量界面不会变.
 
 
   data: {
    clickNum:0,      // 点击次数
    useTime:0,       // 游戏时间  
    checked:0,       // 已匹配牌数
    allCard:allCard,    // 全部卡牌数组
    backImage:backCardImage, // 牌背面 图片地址
    modalHidden: true,    // 游戏完成提示是否显示
    firstX:-1,        // 点击的第一张卡牌的坐标 
    firstY:-1,
    cards:[],        // 随机挑选出来的牌   
    size:8 ,        // 界面显示的牌数=size*2
    clickable:false,    // 当前是否可点击
    timer:''        // 游戏计时的定时器
  }游戏初始化时随机从牌堆中挑出一些,打乱显示在界面上几秒,然后翻回去游戏正式开始.
   startGame:function(){  // 开始游戏
      var data = this.data;
      var that = this;
      console.log('startGame');
      var tmp = this.data.allCard.sort(
          function(a,b){ return Math.random()>.5 ? -1 : 1;}).splice(0,Math.floor(data.size)); // 打乱牌堆,挑出size/2张牌
      tmp = tmp.concat(tmp).sort(function(a,b){ return Math.random()>.5 ? -1 : 1;}); // 牌*2,再打乱
      // 添加图片src和卡牌翻转状态state,存在二维数组方便展示
      var cards = [];
      var ix = -1;   var iy = 0;
      for(var i in tmp){
        if( i%4 == 0){
          cards.push([]);
          ix++; iy = 0;
        }
        cards[ix].push({
            src:'../images/' +tmp[i]+ '.jpg',
            state:1   // 为1时显示图片,为0时显示牌背面
            });
      }
      this.data.cards = cards;
      this.setData({
        cards:cards,
        clickNum:0,
        useTime:0,
        checked:0,
        modalHidden:true,
        firstX:-1,
        clickable:false
      });
      var that = this;
      setTimeout(function(){
       that.turnAllBack();  // 所有的牌翻到背面
       console.log('turn all back');
       data.clickable = true;
       if(data.timer === ''){
          data.timer = setInterval(function(){
            data.useTime++;
            that.setData({useTime:data.useTime});
            },1000); // 游戏开始计时
       }else{
          that.setData({useTime:0});
       }
      },5000); // 游戏开始前先让玩家记忆几秒钟
  }
响应点击事件
 
 
 onTap: function(event){
    var that = this;
    var data = this.data;
    var ix = event.currentTarget.dataset.ix; // 获取点击对象的坐标
    var iy = event.currentTarget.dataset.iy; 
    console.log('onTap ' + ix + ' ' + iy);
    if(data.cards[ix][iy].state != 0 || !data.clickable)  // 点击的不是未翻过来的牌或者现在不让点直接pass
      return;
    that.setData({clickNum:++data.clickNum}); //点击数加1   
    // 1. 检测是翻过来的第几张牌
    if(data.firstX == -1){
    // 1.1 第一张修改状态为 1
      data.cards[ix][iy].state = 1;
      data.firstX = ix; data.firstY = iy;  // 记下坐标
      that.setData({cards:data.cards});     // 通过setData让界面变化
    }else{
      // 1.2 前面已经有张牌翻过来了,先翻到正面然后看是不是一样
      data.cards[ix][iy].state = 1;
      that.setData({cards:data.cards});
      if(data.cards[data.firstX][data.firstY].src === data.cards[ix][iy].src){
        // 1.2.1.1 两张牌相同, 修改两张牌的state为2完成配对
        data.cards[data.firstX][data.firstY].state = 2;
        data.cards[ix][iy].state = 2;
        data.checked += 1; // 完成配对数++
        data.firstX = -1; // 准备下一轮匹配 
        // 1.2.1.2 检查是否所有牌都已经翻过来,都已翻过来提示游戏结束
        if(data.checked == data.size){ // 所有牌都配对成功了!
            this.setData({ modalHidden: false});     
            clearInterval(this.data.timer); // 暂停计时
            this.data.timer = '';
            this.saveScore({'time':data.useTime,'click':data.clickNum}) // 保存成绩
         }
      }else{  // 1.2.2 两张牌不同, 修改两张牌的state为 0
         data.cards[data.firstX][data.firstY].state = 0; 
         data.cards[ix][iy].state = 0;
         data.firstX = -1;
         data.clickable = false;
         setTimeout(function(){
          that.setData({cards:data.cards,clickable:true});
         },500); //过半秒再翻回去
      }
    } 
    console.log(this.data.cards);
  }把所有牌翻到反面
 turnAllBack:function(){
      for(var ix in  this.data.cards)
        for(var iy in this.data.cards[ix])
          this.data.cards[ix][iy].state = 0;
      this.setData({ cards:this.data.cards });
 }游戏结束时,对游戏成绩进行排名然后通过wx.getStorageSync保存到本地
 
 
 saveScore:function(score){ // 保存分数
     var maxscore = wx.getStorageSync('maxscore');
     if(maxscore == undefined || maxscore == '')
      maxscore = [];
     maxscore.push(score);
     maxscore = maxscore.sort(function(a,b){ 
         if(a.time < b.time )
            return -1;
        else if( a.time == b.time && a.click < b.click)
            return -1;
        else return 1;
     });
     wx.setStorageSync( 'maxscore',maxscore);
 }进入页面时记得调用startGame
   onLoad: function () {
    this.startGame();
    console.log(this.data.cards);
  }2.界面布局
  
 
  用view标签建立游戏的主界面
  
 
  首先在顶部新建两个view 来显示时间和点击次数, 时间和点击次数是动态变化的所以要用两个大括号包住变量名这种格式
  
 
  <view class="score">
    <view class="scoredetail">
        <view class="scoredesc">时间</view>
        <view class="scorenumber">{{useTime}}</view>
    </view>
    <view class="scoredetail">
        <view class="scoredesc">点击次数</view>
        <view class="scorenumber">{{clickNum}}</view>
    </view>
</view>
  
  
   然后是卡牌,游戏的卡牌保存在二维数组中,用view标签for方法把二维数组展开.for-index 是当前元素索引值, for-item表示当前元素的引用,.同时通过state的值来控制牌的display样式是none还是block
  
  
   <view class="">
  <view class="board" >
    <view class="rows" wx:for="{{cards}}" wx:for-index="idx" wx:for-item="row">
        <view wx:for="{{row}}" class="cols"   wx:for-index="idy"  wx:for-item="card" >
            <view  class="" data-ix="{{idx}}"  data-iy="{{idy}}"  bindtap="onTap" >
                <image class="card" style="display:{{card.state == 0 ? 'none' : 'block'}}" mode="scaleToFill" src= "{{card.src}}" data-card="{{card}}"></image> <!--牌正面-->
                <image class="card back" style="display:{{card.state != 0 ? 'none' : 'block'}}" mode="scaleToFill" src= "{{backImage}}" ></image> <!--牌背面-->
            </view>
        </view>
    </view>
    </view>
</view>
  
  
   相当于 javascript的
  
  
   for(row in cards){
     for(card in row){
          <image>...</image>          <image>...</image> }}
最后用model标签来弹出游戏结束时的提示, hidden属性用来控制modal是否显示,bindconfirm是点击确认时调用的方法, bindcancel是点取消时的方法, 这里点取消时我们让游戏跳转到排名页.
    
     <modal class="modal" hidden="{{modalHidden}}" bindconfirm="modalComfirm" bindcancel="modalCancle" cancelText="查看排名">
  <view> 游戏结束 ,重新开始吗? </view>
</modal>在game.js中加两个响应函数
    
    
     modalComfirm:function(){
    this.startGame();
  },
modalCancle:function(){
    this.setData({
      modalHidden: true,
    })
   wx.navigateTo({
      url: '../logs/logs'
    })
  }3.样式文件
    
    
     .score {
    display: flex;
    font-size: 1vh;
}
.scoredetail{
    flex: 1;
    height: 150rpx;
    background:rgba(185, 65, 189, 0.8);
    /*opacity: 0.6; */
    margin: 50rpx 20rpx 40rpx 20rpx;
    text-align: center;
    border-radius: 11rpx;
}
.scoredetail:last-child{
    margin-right: 40rpx;
}
.scoredesc{
    font-size: 0.9rem;
    line-height: 50rpx;
    margin-top: 10rpx;
    color:yellow;
}
.scorenumber{
    line-height: 60rpx;
     font-size: 1.1rem;
     margin-top: 5rpx;
     color:yellow;
}
.board{
    margin-left: 5rpx;
    margin-bottom: 5rpx;
}
.rows{
    margin-bottom:1rpx ;
    padding:0rpx;
    text-algin:center;
    flex-direction:row;
    display:flex;
}
.cols{
    margin: 5rpx;
}
.card{
     width:  175rpx;
     height: 237rpx;
     padding:1rpx;
     magrin: 2rpx;
}
page{background: url('../images/gamebg.jpg') center top; }
     
     
     
     
四、最后把logs页面改造成游戏成绩页
    
    
     在进入log页时用wx.getStorageSync 读取之前保存在游戏成绩信息
     
      //logs.js
Page({
  data: {
    logs: []
  },
  onLoad: function () {
    this.setData({
      logs: (wx.getStorageSync('maxscore') || [])
      })
  }
})
     
     
      游戏成绩读取出来是一个已经排好序的数组, 所以直接用wx:for按顺序显示出来就行
     
     
      <!--logs.wxml-->
<view class="container log-list">
 <text class="log-item">    用时  点击</text>
  <block wx:for="{{logs}}" wx:for-item="log" wx:key="*this">
    <text class="log-item">{{index + 1}}. {{log.time}}秒 {{log.click}}次</text>
  </block>
</view>
      
      
      
     
    
  然后就大功告成啦
 
 
  
  
 
 
  
 
 
  游戏完整代码
 
 
  
   https://github.com/kwdhd/wxFpgame
   
  
  
 
  
 
 开发时参考了这个版本
 
 
 http://blog.csdn.net/fdipzone/article/details/8630427
 
 
 
                
        
    
                    转载请注明原文地址: https://ju.6miu.com/read-6947.html