0基礎編寫微信小程序-2048小游戲


博客班級  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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM