前言
我是深鸿会的一个学习小组组长,同时也是深大数院的一名学生。在上一周咱们小组观看并学习了张荣超老师在鸿蒙学院发布的视频,并且在视频里学到了不少知识,同时咱们也收到了启发。因而咱们运用张老师视频里教过的知识独立开发了一个小游戏“数字华容道”。如下是参与开发的小组成员:
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; }
总结
以上就是咱们小组对于数字华容道的开发过程,其中也包括了咱们的一些心得与学习体验,也有咱们在开发过程当中遇到的难题。总的来讲,此次数字华容道的开发对于咱们小组来也算说是一次宝贵的学习经验。