前言
我是深鴻會的一個學習小組組長,同時也是深大數院的一名學生。在上一週咱們小組觀看並學習了張榮超老師在鴻蒙學院發佈的視頻,並且在視頻裏學到了不少知識,同時咱們也收到了啓發。因而咱們運用張老師視頻裏教過的知識獨立開發了一個小遊戲「數字華容道」。如下是參與開發的小組成員:
likaijie12138
JiaYi__xixi
wx13415801099
GTH1144754040javascript
概述
數字華容道是用盡可能少的步數,儘可能短的時間,將棋盤上的數字方塊,按照從左到右、從上到下的順序從新排列整齊。而咱們要作的就是把這個小遊戲移植到鴻蒙系統上。如下是咱們所完成的效果圖
1.模式介紹
css
2.玩法介紹
java
代碼分析
主頁面
先上效果圖
算法
這個界面的組成爲兩個部分:華容道圖片和開始遊戲的按鈕。
下面我將一步一步講解如何完成這兩個部分。json
插入「華容道「圖像
找到default文件夾,建立一個新的文件夾「common」,將準備好的logo圖片粘貼到common文件夾中。
打開index.hml,在canvas
代碼以下:
<image src="/common/3.png" class="img"/>
打開index.css,刪除原有的title類,建立一個img類,將圖片的參數輸入,(這裏只輸入了寬度和高度)。
代碼以下:數組
.img{ width: 250px; height:250px; }
打開index.js,將data中的內容刪除。
至此,就能夠達到以下的效果。dom
「開始遊戲「按鈕的設置
打開index.hml,在函數
代碼以下:
<input type="button" value="開始遊戲" class ="btn" onclick="clickAction" />
打開index.css,建立一個類btn,調節字體大小(font-size)爲38px,將按鈕的背景顏色(background-color)設爲橘色,按鈕上的文字的顏色(color)和按鈕邊框上的顏色(border-color)都設爲黑色,最後調節按鈕的寬度(width)爲200px,高度(height)爲50px。
代碼以下:
.學習
btn{ font-size:38px; background-color: #F8C387; color: #000000; border-color: #000000; width:200px; height:50px; }
至此,就能夠達到以下效果圖。
頁面跳轉
接下來咱們將要實現的是,點擊「開始遊戲「按鈕,跳轉到下一個界面,用戶選擇遊戲難度。模式分爲三種:簡單模式、普通模式、困難模式。
效果圖以下
總共要實現三個功能:按下按鈕跳轉頁面,設置一個滾動選擇器,設置「確認」按鈕
點擊按鈕後的頁面跳轉
在pages文件夾下建立一個新的js page,命名爲xuanze。
建立完後能夠在config.json中的pages看到自動添加了剛建立的文件夾的路徑。
打開index.js,先插入一個含有router函數的頭文件,建立一個函數clickAction,添加router.replace函數,裏面的參數uri的值爲跳轉頁面的路徑。
代碼以下:
import router from '@system.router'; clickAction(){ router.replace({ uri:'pages/xuanze/xuanze' });
設置一個滾動選擇器
打開xuanze.hml,建立一個名爲container1的區域組件,組件包含一個名爲container2的區域組件和一個按鈕組件。
<div class="container1"> <div class="container2"> </div> </div>
建立一個名爲pv1的滾動選擇器,裏面的數據picker1range經過動態綁定({ { }})的方式得到,把selected設置爲"簡單模式"使得初始選擇簡單模式,建立一個名爲changeAction1的onchange事件,當滾動選擇器中的數據發生改變的時候,會引起onchange事件。
<picker-view class="pv1" range="{ {picker1range}}" selected="簡單模式" onchange="changeAction1"/>
打開xuanze.css,設置樣式
.container1 { flex-direction: column; justify-content: center; align-items: center; width: 454px; height: 454px; } .container2{ flex-direction:row; justify-content:center; align-items:center; margin-top: 50ox; width:454px; height:250px; } .pv1{ width:300px; height:300px; }
打開xuanze.js,把數據賦予動態綁定的滾輪選擇器中
export default { data: { picker1range:["簡單模式","普通模式","困難模式"], } }
設置「進入遊戲「按鈕
在第一個進入遊戲界面時,咱們已經學過了如何去設置一個「遊戲開始「的按鈕,因此設置」確認「按鈕的步驟跟前面設置按鈕的步驟基本一致,因此這裏就再也不重複了,若是忘了怎麼設置按鈕的小夥伴能夠往前翻翻,有詳細解析。而後咱們打開xuanze.js,輸入如下代碼
clickAction(){ router.replace({ uri:'pages/youxi/youxi', params: { "data": pickervalue} }); }, changeAction1(pv){ console.log("選中項:" + pv.newValue); pickervalue = pv.newValue; }
第一個方法可以在跳轉頁面的同時把滑動選擇器選擇到的值經過字典的方式傳遞到下一個頁面。第二個方法是在滑動選擇器改變後得到滑動選擇器改變後的值。
遊戲界面
在pages文件夾下建立一個新的js page,命名爲youxi。
建立界面
在建立遊戲的時候,咱們首先要畫出一個棋盤,以下圖所示。
首先咱們打開youxi.hml,添加下面這段代碼
<canvas class="canvas" ref="canvas" onswipe="swipeGrids"></canvas>
這樣咱們就建立了一個畫布組件,而ref至關於畫布組件的畫筆,而onswipe爲滑動事件,當咱們手指在手錶上面滑動時會引起swipeGrids()事件。
而後咱們打開youxi.css,來爲咱們的canvas組件定製形狀
.canvas { width: 305px; height: 305px; background-color:#CD853F; }
爲了讓畫布上能顯示出咱們想要的數字,咱們建立三個數組來儲存方格上面的數字
initGrids3() { grids_3 = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]; }, initGrids4(){ grids_4=[[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,0]]; }, initGrids5(){ grids_5=[[1,2,3,4,5], [6,7,8,9,10], [11,12,13,14,15], [16,17,18,19,20], [21,22,23,24,0]]; },
當選擇界面的滑動選擇器選擇不一樣內容的時候,可以畫出不一樣的表格,咱們把傳遞過來的值賦予給num,並創建對應數字的數組。
onInit() { pickervalue=this.data if(pickervalue=="簡單模式"){ num=3; this.initGrids3(); } if(pickervalue=="普通模式"){ num=4; this.initGrids4(); } if(pickervalue=="困難模式"){ num=5; this.initGrids5(); } this.addToGrids() },
到了最關鍵的時候了,怎麼把數組亂序?我嘗試過把二維數組轉化爲一維數組,而後用洗牌算法來把一維數組亂序,再把一維數組轉化爲二維數組。可是出現了一個問題,就是我這樣亂序的話會出現一種無解的狀況。我以3*3爲例,這樣亂序的狀況下,會出現前兩行是恢復正常的,可是最後一行的7與8是交互了的。我查了不少資料,資料都顯示這種狀況是無解的,因此這種算法是有弊端的。因而我用了另外一種算法:隨機出[0,3]的一個整數,當出現0的時候,向左滑一下,當出現1的時候向右滑一下…這樣以此類推,而後循壞不少次,這樣能夠保證亂序出來的數組是能夠有解的,亂序代碼以下:
addToGrids(){ let row_0; let column_0; let random; if(num==3){ for(let i=0;i<num*num*num;i++){ random=Math.floor(Math.random() * 4); for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { if(grids_3[row][column]==0){ row_0=row; column_0=column; } } } if(random==0||random==1){ if(random==0){ if(column_0!=num-1){ let temp=grids_3[row_0][column_0]; grids_3[row_0][column_0]=grids_3[row_0][column_0+1]; grids_3[row_0][column_0+1]=temp; } }else{ if(column_0!=0){ let temp=grids_3[row_0][column_0]; grids_3[row_0][column_0]=grids_3[row_0][column_0-1]; grids_3[row_0][column_0-1]=temp; } } } if(random==2||random==3){ if(random==2){ if(row_0!=num-1){ let temp=grids_3[row_0][column_0]; grids_3[row_0][column_0]=grids_3[row_0+1][column_0]; grids_3[row_0+1][column_0]=temp; } }else{ if(row_0!=0){ let temp=grids_3[row_0][column_0]; grids_3[row_0][column_0]=grids_3[row_0-1][column_0]; grids_3[row_0-1][column_0]=temp; } } } } } if(num==4){ for(let i=0;i<num*num*num;i++){ random=Math.floor(Math.random() * 4); for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { if(grids_4[row][column]==0){ row_0=row; column_0=column; } } } if(random==0||random==1){ if(random==0){ if(column_0!=num-1){ let temp=grids_4[row_0][column_0]; grids_4[row_0][column_0]=grids_4[row_0][column_0+1]; grids_4[row_0][column_0+1]=temp; } }else{ if(column_0!=0){ let temp=grids_4[row_0][column_0]; grids_4[row_0][column_0]=grids_4[row_0][column_0-1]; grids_4[row_0][column_0-1]=temp; } } } if(random==2||random==3){ if(random==2){ if(row_0!=num-1){ let temp=grids_4[row_0][column_0]; grids_4[row_0][column_0]=grids_4[row_0+1][column_0]; grids_4[row_0+1][column_0]=temp; } }else{ if(row_0!=0){ let temp=grids_4[row_0][column_0]; grids_4[row_0][column_0]=grids_4[row_0-1][column_0]; grids_4[row_0-1][column_0]=temp; } } } } } if(num==5){ for(let i=0;i<num*num*num;i++){ random=Math.floor(Math.random() * 4); for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { if(grids_5[row][column]==0){ row_0=row; column_0=column; } } } if(random==0||random==1){ if(random==0){ if(column_0!=num-1){ let temp=grids_5[row_0][column_0]; grids_5[row_0][column_0]=grids_5[row_0][column_0+1]; grids_5[row_0][column_0+1]=temp; } }else{ if(column_0!=0){ let temp=grids_5[row_0][column_0]; grids_5[row_0][column_0]=grids_5[row_0][column_0-1]; grids_5[row_0][column_0-1]=temp; } } } if(random==2||random==3){ if(random==2){ if(row_0!=num-1){ let temp=grids_5[row_0][column_0]; grids_5[row_0][column_0]=grids_5[row_0+1][column_0]; grids_5[row_0+1][column_0]=temp; } }else{ if(row_0!=0){ let temp=grids_5[row_0][column_0]; grids_5[row_0][column_0]=grids_5[row_0-1][column_0]; grids_5[row_0-1][column_0]=temp; } } } } } },
把數組亂序後,咱們onReady()內設置好畫筆,在onShow()中把表格畫出來。其中因爲咱們不一樣模式須要畫的表格都是不同的。因此我根據張榮超老師的2048視頻所講的內容進行推斷,推出SIDELEN 與所畫表格的行數列數的關係爲
SIDELEN = 300/num-5,
而後根據不一樣的num我能夠畫出不一樣的正方形方格出來。
onReady(){ context = this.$refs.canvas.getContext('2d'); }, onShow(){ this.drawGrids(); }, drawGrids(){ const SIDELEN = 300/num-5; if(num==3){ for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { let gridStr = grids_3[row][column].toString(); context.fillStyle=colors["3"]; let leftTopX = column * (MARGIN + SIDELEN) + MARGIN; let leftTopY = row * (MARGIN + SIDELEN) + MARGIN; context.fillRect(leftTopX, leftTopY, SIDELEN, SIDELEN); context.font = "36px HYQiHei-65S"; if (gridStr != "0") { context.fillStyle = "#FFFFFF"; let offsetX = (4 - gridStr.length) * (SIDELEN / 8); let offsetY = (SIDELEN - 24) / 2; context.fillText(gridStr, leftTopX + offsetX, leftTopY + offsetY); } } } } if(num==4){ for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { let gridStr = grids_4[row][column].toString(); context.fillStyle=colors["4"]; let leftTopX = column * (MARGIN + SIDELEN) + MARGIN; let leftTopY = row * (MARGIN + SIDELEN) + MARGIN; context.fillRect(leftTopX, leftTopY, SIDELEN, SIDELEN); context.font = "30px HYQiHei-65S"; if (gridStr != "0") { context.fillStyle = "#FFFFFF"; let offsetX = (4 - gridStr.length) * (SIDELEN / 8); let offsetY = (SIDELEN - 24) / 2; context.fillText(gridStr, leftTopX + offsetX, leftTopY + offsetY); } } } } if(num==5){ for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { let gridStr = grids_5[row][column].toString(); context.fillStyle=colors["5"]; let leftTopX = column * (MARGIN + SIDELEN) + MARGIN; let leftTopY = row * (MARGIN + SIDELEN) + MARGIN; context.fillRect(leftTopX, leftTopY, SIDELEN, SIDELEN); context.font = "24px HYQiHei-65S"; if (gridStr != "0") { context.fillStyle = "#FFFFFF"; let offsetX = (4 - gridStr.length) * (SIDELEN / 8); let offsetY = (SIDELEN - 24) / 2; context.fillText(gridStr, leftTopX + offsetX, leftTopY + offsetY); } } } } },
這樣咱們就把咱們所須要的方格在畫布上畫了出來。
添加功能
爲了實現數字華容道的功能,咱們須要在畫布上添加滑動事件
<canvas class="canvas" ref="canvas" onswipe="swipeGrids"></canvas>
onswipe即爲咱們所添加的滑動事件,在youxi.js中滑動事件的名字爲swipeGrids。當手指滑動的時候,咱們須要作的就是把滑動的方向傳遞進滑動事件swipeGrids裏。而後尋找0方格所在的位置,同時判斷0方格能不能被移動,當0方格能被移動的時候,把0與滑動方向臨近那個數字交換,這樣就完成了一次滑動。代碼以下
swipeGrids(event) { let newGrids = this.changeGrids(event.direction); if(num==3){ grids_3 = newGrids; } if(num==4){ grids_4 = newGrids; } if(num==5){ grids_5 = newGrids; } this.drawGrids(); }, changeGrids(direction){ let row_0; let column_0; if(num==3){ let newGrids_3=grids_3; for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { if(newGrids_3[row][column]==0){ row_0=row; column_0=column; } } } if(this.isShow==false){ if(direction=='left'||direction=='right'){ if(direction=='left'){ if(column_0!=num-1){ let temp=newGrids_3[row_0][column_0]; newGrids_3[row_0][column_0]=newGrids_3[row_0][column_0+1]; newGrids_3[row_0][column_0+1]=temp; this.step+=1; } }else{ if(column_0!=0){ let temp=newGrids_3[row_0][column_0]; newGrids_3[row_0][column_0]=newGrids_3[row_0][column_0-1]; newGrids_3[row_0][column_0-1]=temp; this.step+=1; } } } if(direction=='up'||direction=='down'){ if(direction=='up'){ if(row_0!=num-1){ let temp=newGrids_3[row_0][column_0]; newGrids_3[row_0][column_0]=newGrids_3[row_0+1][column_0]; newGrids_3[row_0+1][column_0]=temp; this.step+=1; } }else{ if(row_0!=0){ let temp=newGrids_3[row_0][column_0]; newGrids_3[row_0][column_0]=newGrids_3[row_0-1][column_0]; newGrids_3[row_0-1][column_0]=temp; this.step+=1; } } } } return newGrids_3; } if(num==4){ let newGrids_4=grids_4; for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { if(newGrids_4[row][column]==0){ row_0=row; column_0=column; } } } if(this.isShow==false){ if(direction=='left'||direction=='right'){ if(direction=='left'){ if(column_0!=num-1){ let temp=newGrids_4[row_0][column_0]; newGrids_4[row_0][column_0]=newGrids_4[row_0][column_0+1]; newGrids_4[row_0][column_0+1]=temp; this.step+=1; } }else{ if(column_0!=0){ let temp=newGrids_4[row_0][column_0]; newGrids_4[row_0][column_0]=newGrids_4[row_0][column_0-1]; newGrids_4[row_0][column_0-1]=temp; this.step+=1; } } } if(direction=='up'||direction=='down'){ if(direction=='up'){ if(row_0!=num-1){ let temp=newGrids_4[row_0][column_0]; newGrids_4[row_0][column_0]=newGrids_4[row_0+1][column_0]; newGrids_4[row_0+1][column_0]=temp; this.step+=1; } }else{ if(row_0!=0){ let temp=newGrids_4[row_0][column_0]; newGrids_4[row_0][column_0]=newGrids_4[row_0-1][column_0]; newGrids_4[row_0-1][column_0]=temp; this.step+=1; } } } } return newGrids_4; } if(num==5){ let newGrids_5=grids_5; for (let row = 0; row < num; row++) { for (let column = 0; column < num; column++) { if(newGrids_5[row][column]==0){ row_0=row; column_0=column; } } } if(this.isShow==false){ if(direction=='left'||direction=='right'){ if(direction=='left'){ if(column_0!=num-1){ let temp=newGrids_5[row_0][column_0]; newGrids_5[row_0][column_0]=newGrids_5[row_0][column_0+1]; newGrids_5[row_0][column_0+1]=temp; this.step+=1; } }else{ if(column_0!=0){ let temp=newGrids_5[row_0][column_0]; newGrids_5[row_0][column_0]=newGrids_5[row_0][column_0-1]; newGrids_5[row_0][column_0-1]=temp; this.step+=1; } } } if(direction=='up'||direction=='down'){ if(direction=='up'){ if(row_0!=num-1){ let temp=newGrids_5[row_0][column_0]; newGrids_5[row_0][column_0]=newGrids_5[row_0+1][column_0]; newGrids_5[row_0+1][column_0]=temp; this.step+=1; } }else{ if(row_0!=0){ let temp=newGrids_5[row_0][column_0]; newGrids_5[row_0][column_0]=newGrids_5[row_0-1][column_0]; newGrids_5[row_0-1][column_0]=temp; this.step+=1; } } } } return newGrids_5; } },
這樣咱們就完成了滑動事件的建立。爲了讓遊戲有趣一點,咱們決定給遊戲添加一個計步數功能。打開youxi.hml,添加txt組件,組件內容爲步數,步數的值經過動態綁定的方式由youxi.js得到。
<text class="step"> 步數:{ { step}} </text>
在youxi.js中初始化步數的值爲0
data: { step:0, isShow:false },
在滑動的時候,每滑動一次,步數就添加1,以下代碼所示
if(direction=='left'||direction=='right'){ if(direction=='left'){ if(column_0!=num-1){ let temp=newGrids_3[row_0][column_0]; newGrids_3[row_0][column_0]=newGrids_3[row_0][column_0+1]; newGrids_3[row_0][column_0+1]=temp; this.step+=1; } }else{ if(column_0!=0){ let temp=newGrids_3[row_0][column_0]; newGrids_3[row_0][column_0]=newGrids_3[row_0][column_0-1]; newGrids_3[row_0][column_0-1]=temp; this.step+=1; } } }
這樣咱們完成了數字華容道的滑動功能和計數功能,接下來咱們將爲數字華容道加入遊戲結束的功能。
打開youxi,hml,添加一個組件在畫布上面,並在這個組件裏包含有一個文本組件和一個控制文本組件是否顯示的組件。文本是否顯示經過動態綁定的方式由youxi.js得到
<stack class="stack"> <canvas class="canvas" ref="canvas" onswipe="swipeGrids"></canvas> <div class="subcontainer" show="{ {isShow}}"> <text class="gameover"> 遊戲結束 </text> </div> </stack>
打開youxi.js,先把文本組件設爲不可見。
data: { step:0, isShow:false },
而後寫一個方法GameOver,當遊戲結束時,並把文本組件變爲可見。
swipeGrids(event) { let newGrids = this.changeGrids(event.direction); if(num==3){ grids_3 = newGrids; } if(num==4){ grids_4 = newGrids; } if(num==5){ grids_5 = newGrids; } this.drawGrids(); if(this.GameOver()==true){ colors = THEME.faded; this.drawGrids(); this.isShow = true; } }, GameOver(){ if(num==3){ for(let row=0;row<num;row++){ for(let column=0;column<num;column++){ if(row!=num-1||column!=num-1) { if (grids_3[row][column] != row * num + (column + 1)) { return false } } } } return true } if(num==4){ for(let row=0;row<num;row++){ for(let column=0;column<num;column++){ if(row!=num-1||column!=num-1) { if (grids_4[row][column] != row * num + (column + 1)) { return false } } } } return true } if(num==5){ for(let row=0;row<num;row++){ for(let column=0;column<num;column++){ if(row!=num-1||column!=num-1) { if (grids_5[row][column] != row * num + (column + 1)) { return false } } } } return true } },
這樣咱們就完成了遊戲結束的顯示了,爲了使遊戲結束時,遊戲不能再繼續進行了,咱們須要在滑動事件里加一個斷定,當文本組件爲不可見的時候,才能進行滑動操做,代碼示例以下
if(this.isShow==false){ if(direction=='left'||direction=='right'){ if(direction=='left'){ if(column_0!=num-1){ let temp=newGrids_3[row_0][column_0]; newGrids_3[row_0][column_0]=newGrids_3[row_0][column_0+1]; newGrids_3[row_0][column_0+1]=temp; this.step+=1; } }else{ if(column_0!=0){ let temp=newGrids_3[row_0][column_0]; newGrids_3[row_0][column_0]=newGrids_3[row_0][column_0-1]; newGrids_3[row_0][column_0-1]=temp; this.step+=1; } } }
爲了使遊戲畫面好看一點,咱們的方格須要不一樣的顏色。同時當遊戲結束的時候,把顏色變淡一點,以方便看出來遊戲結束已經結束了。首先咱們建立兩個字典normal和faded,字典的鍵和元素都爲數字以及數字對應的顏色,當遊戲未結束,顏色用normal字典的顏色,當遊戲結束的時候,顏色字典變爲faded,同時從新繪製一次方格。
const THEME = { normal: { "3":"#FB8B05", "4":"#2775B6", "5":"#DD8AF8" }, faded:{ "3":"#E3BD8D", "4":"#66A9C9", "5":"#F8D1EE" } }; var colors = THEME.normal; if(this.GameOver()==true){ colors = THEME.faded; this.drawGrids(); this.isShow = true; }
總結
以上就是咱們小組對於數字華容道的開發過程,其中也包括了咱們的一些心得與學習體驗,也有咱們在開發過程當中遇到的難題。總的來講,此次數字華容道的開發對於咱們小組來也算說是一次寶貴的學習經驗。