博客班级  https://edu.cnblogs.com/campus/zjcsxy/SE2020/
作业要求  https://edu.cnblogs.com/campus/zjcsxy/SE2020/homework/11334
作业目标
  • 编写一个小程序,可以全新编写,也可以学习别人的小程序进行修改
  • 熟悉git代码管理流程,将源代码上传到到github
  • 在博客园班级中写一篇相应的博文
作业源代码 https://github.com/risedMer/2048-miniprogram
学号  31801120
姓名 梅景添
院系 浙大城市学院计算机系 

 

前言:软件工程的第一次作业,主要难点在于2048的计算算法,本文主要讲解微信小程序版本的2048小游戏编写,含源代码和注解。

最终版本的地址:https://github.com/risedMer/2048-miniprogram

开发工具:微信开发者工具

=====================================

2020.10.21更新

在原有的小程序基础上新增了三个界面

界面一:开始游戏界面,使游戏不再已进入就启动

界面二:关于界面,关于程序开发的基础信息

界面三:最高分界面,可查看历史最高分

新增界面的内容不涉及有技术的算法操作,仅在样式以及界面上增加内容,故代码补贴出,完整代码见底部github链接

=======================================

效果演示

 

 

全局配置

 app.json:

 enablePullDownRefresh务必设置为false,以禁止小程序下拉刷新

 其余三个的值可依个人喜好自行设置,注:backgroundTextStyle仅支持dark/light

 具体配置可参见官方文档: https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html

 1 {
 2   "pages": [
 3     "pages/start/start",
 4     "pages/index/index",
 5     "pages/maxScore/maxScore",
 6     "pages/about/about"
 7   ],
 8   "window": {
 9     "backgroundTextStyle": "light",         //小程序导航栏标题高亮
10     "navigationBarBackgroundColor": "#000000",   //小程序导航栏颜色(16进制)
11     "navigationBarTitleText": "Mer\'s 2048",    //小程序导航栏标题
12     "enablePullDownRefresh": false          //禁止小程序页面下拉刷新
13   },
14   "sitemapLocation": "sitemap36.json"
15 }

 

 app.js

 小程序启动时从缓存中读取日期信息

1 App({
2   onLaunch: function () {
3     var logs = wx.getStorageSync(\'logs\') || []
4     logs.unshift(Date.now())
5     wx.setStorageSync(\'logs\', logs)
6   }
7 })

 app.wxss

 全局样式配置文件

1 .container {
2   height: 100%;
3   display: flex;
4   flex-direction: column;
5   align-items: center;
6   justify-content: space-between;
7   padding: 200rpx 0;
8   box-sizing: border-box;
9 } 

小程序核心部分

 以上内容为小程序的全局配置,用于小程序的整体配置,以下内容进入正题,主要分为3块内容:

 1. 2048的页面布局(index.wxml)

 2. 2048的样式文件(index.wxss)

 3. 2048的程序运行逻辑,也是该小程序中最为核心且最为复杂的一部分(index.js)

(还有一个index.json文件,由于该小程序未引入其他自定义组件,故该文件不影响小程序的运行,呈默认状态{ “usingComponents”: {}})

 index.wxml

<view class = "action_cavas" bindtouchstart = "tapStart" bindtouchmove = "tapMove" bindtouchend = "tapEnd">
  <view class = "score">
    <view class = "title">2048</view>
    <view class = "scoredetail">
      <view class = "scoredesc">历史最高</view>
      <view class = "scorenumber">{{maxscore}}</view>
    </view>
  </view>
  <view class = "bc_cavas">
    <view class = "bc" wx:for = "{{numbers}}" wx:for-item = "row" >
      <view wx:for = "{{row}}" class = "bc_ bc_{{item}}">{{item}}</view>
    </view>
  </view>
</view>
<modal class = "modal" hidden = "{{modalHidden}}" bindconfirm = "modalChange" bindcancel = "modalCancle">
  <view>游戏结束</view>
</modal>

 index.wxss

  1 .score {
  2   display: flex;
  3 }
  4 
  5 .title{
  6   flex:1;
  7   height: 150rpx;
  8   line-height: 150rpx;
  9   background:#eec22e;
 10   margin: 80rpx 20rpx 40rpx 40rpx;
 11   text-align: center;
 12   font-size: 1.5rem;
 13   color: white;
 14   border-radius: 10rpx;
 15 }
 16 
 17 
 18 .scoredetail{
 19   flex: 1;
 20   height: 150rpx;
 21   background:#eee4da;
 22   margin: 80rpx 20rpx 40rpx 20rpx;
 23   text-align: center;
 24   border-radius: 10rpx;
 25 }
 26 
 27 .scoredetail:last-child{
 28   margin-right: 40rpx;
 29 }
 30 
 31 .scoredesc{
 32   font-size: 0.8rem;
 33   line-height: 60rpx;
 34 }
 35 .scorenumber{
 36   line-height: 70rpx;
 37 }
 38 
 39 .bc_{
 40 height: 152rpx;
 41 width: 152rpx;
 42 margin: 6rpx 6rpx;
 43 line-height: 152rpx;
 44 text-align: center;
 45 font-size: 60rpx;
 46 color: #fff7eb;
 47 }
 48 .bc_0{
 49   color:#ccc0b2;
 50   background: #ccc0b2;
 51 }
 52 .bc_2
 53 {
 54   color: #7c736a;
 55   background: #eee4da;
 56 }
 57 .bc_4
 58 {
 59   color: #7c736a;
 60   background: #ece0c8;
 61 }
 62 .bc_8
 63 {
 64   color: #fff7eb;
 65   background: #f2b179;
 66 }
 67 .bc_16
 68 {
 69   color:#fff7eb;
 70   background:#f59563;
 71 }
 72 .bc_32
 73 {
 74   color:#fff7eb;
 75   background:#f57c5f;
 76 }
 77 .bc_64
 78 {
 79   color:#fff7eb;
 80   background:#f65d3b;
 81 }
 82 .bc_128
 83 {
 84   color:#fff7eb;
 85   background:#edce71;
 86 }
 87 .bc_256
 88 {
 89   color:#fff7eb;
 90   background:#edcc61;
 91 }
 92 .bc_512
 93 {
 94   color:#fff7eb;
 95   background:#ecc850;
 96 }
 97 .bc_1024
 98 {
 99   color:#fff7eb;
100   background:#edc53f;
101 }
102 .bc_2048
103 {
104   color:#fff7eb;
105   background:#eec22e;
106 }
107 .bc{
108 display: flex;
109 }
110 
111 .bc_cavas{
112   display: flex;
113   height: 670rpx;
114   background-color: #b8af9e;
115   justify-content: center;
116   align-content: center;
117   flex-wrap:wrap;
118   margin: 10rpx 40rpx;
119   border-radius: 10rpx;
120 }
121 
122 .action_cavas {
123   width:100%;
124   height: 100%;
125 }
126 
127 .intro{
128   display: flex;
129   margin: 0 60rpx;
130   font-size: small;
131   color: #fff7eb;
132   justify-content:flex-end;
133 }

 index.js

 该部分代码略长,下面将其分开详细讲解

 data部分,2048的数据初始化界面

 1 data: {
 2     score: 0,          //当前得分
 3     maxscore: 0,        //最高分在界面上方显示
 4     startx: 0,         //坐标初始化为0
 5     starty: 0,
 6     endx: 0,
 7     endy: 0,
 8     direction: \'\',
 9     numbers: [[],[],[],[]],   //该嵌套数组用于2048的游戏布局4*4网格,先置空,在接下来的onLoad块中将其初始化
10     modalHidden: true,
11 }

 onLoad:function()

 小程序启动后进入第一个页面首先加载的函数,此处涉及到小程序页面的生命周期,详情可见以下链接:

 https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/page-life-cycle.html

 1 onLoad: function() {
 2      var maxscore = wx.getStorageSync(\'maxscore\')  //从缓存中读取最大值
 3      if(!maxscore) maxscore = 0  //若缓存中没有最大值的记录,则将其置为0
 4      let num = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
 5      let j = 0   //该处循环为4*4网格的初始化,由于实在想不到其他简易的初始化方案,于是选择了random的随机数,并嵌套了几层尝试让初始化的界面不会有很多数字
 6      for(;j < 4;j++) {
 7        num[0][j] = 2 * (Math.floor((Math.random() * Math.floor(Math.random() * 4))))
 8        num[1][j] = 2 * (Math.floor((Math.random() * 2)))
 9        num[2][j] = 2 * (Math.floor((Math.random() * 1)))
10        num[3][j] = 2 * (Math.floor((Math.random() * 2)))
11        num[j][0] = 2 * (Math.floor((Math.random() * Math.floor(Math.random() * 4))))
12        num[j][2] = 2 * (Math.floor((Math.random() * 1)))
13        num[j][1] = 2 * (Math.floor((Math.random() * Math.floor(Math.random() * 4))))
14        num[j][3] = 2 * (Math.floor((Math.random() * 1)))
15      }
16     //将值读取后置给data
17      this.setData({
18        maxscore:maxscore,
19        numbers:num
20      })
21 }

 将玩游戏的过程中产生的最高分存入缓存,并更新游戏界面最上方的最高分分值

1 storeScore:function(){
2       console.log(this.data.maxscore, this.data.score)
3       if(this.data.maxscore < this.data.score){
4       this.setData({
5         maxscore:this.data.score
6         })
7         wx.setStorageSync(\'maxscore\', this.data.maxscore)
8       }
9   },

 触摸屏幕后的动作判定,根据手指在屏幕的初始触点和离开屏幕后的结尾触点计算出是向左、向右、向上、向下四个方向的操作并将值赋给一个dir变量

 1 tapStart: function(event){
 2     this.setData({
 3       startx: event.touches[0].pageX,
 4       starty: event.touches[0].pageY
 5       })
 6   },
 7   
 8   tapMove: function(event){
 9     this.setData({
10       endx: event.touches[0].pageX,
11       endy: event.touches[0].pageY
12       })
13   },
14   tapEnd: function(event){
15     var heng = (this.data.endx) ? (this.data.endx - this.data.startx) : 0;
16     var shu = (this.data.endy) ? (this.data.endy - this.data.starty) : 0;
17     console.log(heng, shu);
18     if(Math.abs(heng) > 5 || Math.abs(shu) > 5){
19       var direction = (Math.abs(heng) > Math.abs(shu)) ? this.computeDir(1, heng):this.computeDir(0, shu);
20       this.setData({
21         startx:0,
22         starty:0,
23         endx:0,
24         endy:0
25       })
26       this.mergeAll(direction) && this.randInsert();
27     }
28   },
29   computeDir: function(heng, num){
30     if(heng) return (num > 0) ? \'right\' : \'left\';
31     return (num > 0) ? \'bottom\' : \'top\';
32   },

 mergeAll函数通过一个switch的操作将手指在屏幕的操作调动起来,来进入各个动作操作的函数,让方块进行合并,计算数值

 1 mergeAll: function(dir){
 2     this.checkGame();
 3     switch(dir){
 4       case \'left\':
 5         return this.mergeleft();
 6         break;
 7       case \'right\':
 8         return this.mergeright();
 9         break;
10       case \'top\':
11         return this.mergetop();
12         break;
13       case \'bottom\':
14         return this.mergebottom();
15         break;
16       default:
17     }
18   },

 以下函数为将方块进行合并的过程,原理均一致故只展示向左滑动时合并方块的过程

 只要会一个方向的数值处理,其他方向和该操作一致,不同处仅符号的变化和x,y轴的变化

 1 mergeleft: function(){
 2     var change = false;
 3     var arr = this.data.numbers;
 4     for(var i = 0; i < 4; i++){
 5       for(var j = 0; j < 3; j++){
 6         if(arr[i][j] == 0) continue;
 7         for(var k = 1; k < 4-j; k++){
 8           if(arr[i][j] != 0 && arr[i][j+k] != 0){
 9             if(arr[i][j] != arr[i][j+k]) break;
10             arr[i][j] = arr[i][j] *2;
11             arr[i][j+k] = 0;
12             change = true;
13             if(this.data.score < arr[i][j]){
14               this.setData({score:arr[i][j]})
15             }
16             break;
17           }
18         }
19       }
20       for(var j = 0; j < 3; j++){
21         if(arr[i][j] == 0){
22           for(var k = 1; k < 4-j; k++){
23             if(arr[i][j+k] != 0){
24               arr[i][j] = arr[i][j+k];
25               arr[i][j+k] = 0;
26               change = true;
27               break;
28             }
29           }
30         }
31       }
32     }
33     this.setData({
34           numbers:arr
35           })
36     this.storeScore()
37     return change
38   },

 随机插入函数

 在每次滑动后随机向一个为0的格子内插入一个数值2的方格

 1 randInsert: function(){
 2     var arr = this.data.numbers
 3     var num = Math.random() < 0.8 ? 2 : 4
 4     var zeros = [];
 5     for(var i = 0; i < 4; i++){
 6       for(var j = 0; j < 4; j++){
 7         if(arr[i][j] == 0){
 8             zeros.push([i, j]);
 9         }
10       }
11     }
12     var position = zeros[Math.floor(Math.random() * zeros.length)];
13     arr[position[0]][position[1]] = num
14     this.setData({
15       numbers:arr
16       })
17   },

 游戏检查函数

 该部分函数的主要作用就是检查游戏是否结束,若每个格子均已有非零数字填充,且相邻的格子无法进行合并,则进入modalCancle函数,弹出游戏结束窗口

 1 checkGame: function(){
 2     var arr = this.data.numbers
 3     for(var i = 0; i < 4; i++){
 4       for(var j = 0; j < 4; j++){
 5         if(arr[i][j] == 0) 
 6           return;
 7       }
 8     }
 9     for(var i = 0; i < 3; i++){
10       for(var j = 0; j < 3; j++)
11         if(arr[i][j] == arr[i+1][j] || arr[i][j] == arr[i][j+1]) 
12           return;
13     }
14     for(var j = 0; j < 3; j++) {
15       if(arr[3][j] == arr[3][j+1]) 
16         return;
17       if(arr[j][3] == arr[j+1][3]) 
18         return;
19     }
20     this.setData({
21       modalHidden: false,
22     })
23   },

 若游戏结束则弹出弹窗询问取消或重新开始

 (重现了一次初始化4*4网格的蛇皮操作)

 1 modalChange:function(){
 2     let num = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
 3     let j = 0
 4     for(;j < 4;j++) {
 5       num[0][j] = 2 * (Math.floor((Math.random() * Math.floor(Math.random() * 4))))
 6       num[1][j] = 2 * (Math.floor((Math.random() * 2)))
 7       num[2][j] = 2 * (Math.floor((Math.random() * 1)))
 8       num[3][j] = 2 * (Math.floor((Math.random() * 2)))
 9       num[j][0] = 2 * (Math.floor((Math.random() * Math.floor(Math.random() * 4))))
10       num[j][2] = 2 * (Math.floor((Math.random() * 1)))
11       num[j][1] = 2 * (Math.floor((Math.random() * Math.floor(Math.random() * 4))))
12       num[j][3] = 2 * (Math.floor((Math.random() * 1)))
13     }
14     this.setData({
15       score: 0,
16       numbers: num,
17       modalHidden: true,
18     })
19   },
20   modalCancle:function(){
21     this.setData({
22       modalHidden: true,
23     })
24 }

 以上即本次作业的全部代码,所有代码均已完整上传至github,链接如下:

 https://github.com/risedMer/2048-miniprogram

 

 若存在不足之处请在评论区随意指出,本人愿虚心求教将此代码更进一步的更新完善。

=============================

2020-10-18

 

版权声明:本文为4years原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/4years/p/13835032.html