2023年夏季《移动软件开发》实验报告
姓名和学号? | |
---|---|
本实验属于哪门课程? | 中国海洋大学23夏《移动软件开发》 |
实验名称? | 实验4:拼图游戏 |
博客地址? | XXXXXXX |
Github仓库地址? | XXXXXXX |
一、实验目标
1、综合应用所学知识创建完整的拼图游戏项目;2、熟练掌握组件。
二、实验步骤
首页功能需求如下:
- 首页需要包含标题和关卡列表。
- 关卡至少要有 6 个关卡选项,每个关卡显示预览图片和第几关。
- 点击关卡列表可以打开对应的游戏画面。
游戏页功能需求如下:
- 游戏页面需要显示游戏提示图、游戏画面和“重新开始”按钮。
- 每关游戏提示图显示对应的图片预览。
- 游戏画面随机将原图打乱为3×3的小方块,并且可移动被点击的方块。
- 点击“重新开始”按钮可以重新随机打乱小方块并开始游戏。
1.项目创建
- 如图所示:
2.页面配置
1.创建页面文件
-
项目创建完毕后,在根目录中会生成文件夹pags用于存放页面文件。一般来说首页默认命名为index,表示小程序运行的第一个页面;其他页面名回称可以自定义。本项目有两个页面文件,需要创建index(首页页面)和game视频讲解(游戏页面)。
-
具体操作如下:
-
将app.json文件内pages属性中的“pags/logs/logs改成“pages/game/game”。
-
按快捷键Ctrl+S保存修改后会在page文件夹下自动生成game目录。
-
2.删除和修改文件
-
具体操作如下:
-
删除utils文件夹及其内部所有内容。
-
删除pages文件夹下的logs目录及其内部所有内容。
-
删除index.wxml和index.wxss中的全部代码。
-
删除index,.js中的全部代码,并且输入关键词page找到第二个选项按回车键让其自动补全函数。
-
删除app.wxss中的全部代码。
-
删除app.js中的全部代码,并且输入关键词app找到第一个选项按回车键让其自动补全函数。
-
3.创建其他文件
-
创建其他自定义文件,创建文件夹用于存放关卡图片素材,文件夹名称为:images,单击目录结构左上角的十号创建文件夹并命名为images。
-
右击目录结构中的images文件夹,选择“硬盘打开”,在该文件夹中将关卡图片文件全部复制、粘贴进去。完成后的目录结构如图所示。
-
配置布局至此全部完成,进行页面和视图设计。
4.视图设计
1.导航栏设计
-
小程序默认导航栏是黑底白字的效果,因此需要在app.json中自定义导航栏标题和背景颜色。更改后的app.json文件代码如下:
"pages":[ "pages/index/index" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#E64340", "navigationBarTitleText": "拼图游戏", "navigationBarTextStyle":"white" },
-
上述代码可以更改所有页面的导航栏标题文本为“拼图游戏”、背景颜色为珊瑚红色(#E64340)。
可得效果如图所示:
2.页面设计(margin:表示上下左右的距离)
- 公共样式设计:首先在app.wxss中设置页面窗口和顶端标题的公共样式,代码如下:
/**app.wxss**/
/*页面容器样式*/
.container {
height: 100vh;
color: #E64340;
font-weight: bold;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
}
/*顶端标题样式*/
.title{
font-size:18pt;
}
- 效果展示如图所示:
-
首页设计:首页主要包含两部分内容,即标题和关卡列表。
计划使用如下组件:
-
顶端标题:容器;
-
关卡列表:容器,内部使用数组循环。
当前效果如图所示:
由图可见,此时可以显示标题和一个临时关卡。由于尚未获得关卡数据,只所以暂时无法显示完整的关卡列表,仅供作为样式参考。
-
-
游戏页面设计:
- 游戏页面需要用户点击首页的关卡列表;
- 然后在新窗口中打开该页面口游戏页面包括游戏提示图、游戏画面和“重新开始”按钮
-
由于暂时没有做点击跳转的逻辑设计,所以可以**在开发工具顶端选择“普通编译”下的“添加编译模式”,并携带临时测试参数level=pic01.jpg,**如图所示:
-
此时预览就可以直接显示game页面了,设计完毕后再改回“普通编译”模式即可重新显示首页。
-
当前效果如图所示:
-
由图可见,此时可以显示完整样式效果。由于尚未获得游戏数据,所以暂时无法根据用户点击的关卡入口显示对应的游戏内容,仅供作为样式参考。此时页面布局与样式设计就已完成。
5.逻辑实现
1.首页逻辑(index)
- 首页主要有两个功能需要实现,一是展示关卡列表,二是点击图片能够跳转到游戏页面。
(1)关卡列表展示:
-
在JS文件的 data 中录入关卡图片的数据信息,这里以6个关卡为例 。
- 相关的JS(pages/index/index.js)代码片段如下:
data: { levels:[ 'pic01.jpg', 'pic02.jpg', 'pic03.jpg', 'pic04.jpg', 'pic05.jpg', 'pic06.jpg' ] },
-
接着为关卡对应的组件添加wx:for属性循环显示关卡列表数据和图片。
-
修改后的WXML(pages/index/index.wxml)代码如下:
<!--index.wxml--> <view class='container'> <!--标题--> <view class='title'>游戏选关</view> <!--关卡列表--> <view class='levelBox'> <view class="box" wx:for='{{levels}}' wx:key='levels{{index}}' bindtap='chooseLeve' date-level='{{item}}'> <image src='/images/{{item}}'></image> <text>第{{index+1}}关</text> </view> </view> </view>
-
- 此时页面效果如图所示:
(2)点击跳转游戏页面:
-
若希望用户点击关卡图片即可实现跳转,需要首先为关卡列表项目添加点击事件。
相关WXML(pages/index/index.wxml)代码相关片段修改如下:
- (表示为关卡添加了自定义点击事件函数chooseLevel,并且使用data–level属性携带了关卡图片信息)
<!--index.wxml--> <view class='container'> <!--标题--> <view class='title'>游戏选关</view> <!--关卡列表--> <view class='levelBox'> <view class='box' wx:for='{{levels}}' wx:key='levels{{index}}' bindtap='chooseLevel' data-level='{{item}}'> <image src='/images/{{item}}'></image> <text>第{{index+1}}关</text> </view> </view> </view>
-
然后在对应的index.js文件中添加chooseLevel函数的内容,代码片段如下:
chooseLevel: function (e) { let level = e.currentTarget.dataset.level wx.navigateTo({ url: '../game/game?level=' + level }) },
-
此时已经可以点击跳转到game页面,并且成功携带了关卡图片数据,但是仍需在game页面进行携带数据的接收处理才可显示正确的游戏画面。
-
2 游戏页逻辑(game)
- 游戏页主要有两个功能需要实现,一是显示提示图;二是游戏逻辑实现。
(1)显示提示图:
-
在首页逻辑中已经实现了页面跳转并携带了关卡对应的图片信息,现在需要在游戏页面接收关卡信息,并显示对应的图片内容。
-
相关JS(pages/game/game.js)代码片段如下:
onLoad: function (options) { //更新图片路径地址 url = '/images/' + options.level //更新提示图的地址 this.setData({ url: url }) },
-
修改WXML(pages/game/game.wxml)代码片段如下:
<!--pages/game/game.wxml--> <view class="container"> <!--提示图区域--> <view class='title'>提示图</view> <image src='{{url}}'></image>
-
- 此时重新从首页点击不同的关卡图片跳转就可以发现已经能够正确显示对应的内容了。
- 运行效果如图所示:
(2)游戏逻辑实现。
1.准备工作。
首先在game.js文件的顶端记录一些游戏初始数据信息。
- 显示结果如图所示:
2.初始化拼图画面。
- 传统做法是随机抽取画面中的任意两个方块,然后交换彼此位置,在进行足够多次数的交换后基本可以实现随机打乱的效果。
- 但是这种方法有一个弊端,就是有时候会陷入无解的死局。
- 因此可以考虑从空白方块的所在位置入手,每次随机让它和周围的邻近方块交换位置,这样可以通过方块反向移动回到最初始状态(确保本局有解),并且在交换足够多的次数后也可以实现随机打乱的效果。
-
在game.js文件中添加shuffle函数,用于重新开始游戏。对应的JS(pages/game/game.js)代码片段添加如下:
shuffle: function () { //先让方块回到初始位置 num = [ ['00', '01', '02'], ['10', '11', '12'], ['20', '21', '22'] ] //记录当前空白方块的行和列 var row = 2 var col = 2 //打乱方块顺序100次 for (var i = 0; i < 100; i++) { var direction = Math.round(Math.random() * 3) if (direction == 0) { if (row != 0) { num[row][col] = num[row - 1][col] num[row - 1][col] = '22' row -= 1 } } else if (direction == 1) { if (row != 2) { num[row][col] = num[row + 1][col] num[row + 1][col] = '22' row += 1 } } else if (direction == 2) { if (col != 0) { num[row][col] = num[row][col - 1] num[row][col - 1] = '22' col -= 1 } } else if (direction == 3) { if (col != 2) { num[row][col] = num[row][col + 1] num[row][col + 1] = '22' col += 1 } } } },
上述代码表示使用for循环进行了100次打乱,开发者可以根据自己的需求更改循环次数。每次使用Math.random()方法从上下、左、右4个方向中随机产生一个方向,之后如果符合条件则交换空白方块和图片方块的位置。
- 然后在game.js中添加自定义函数drawCanvas,用于将打乱后的图片方块绘制到画布上。
- 最后在game.js的onLoad函数中调用自定义函数shuffle和drawCanvas。对应的JS(pages/game/game.js)代码片段添加如下:
Page({
// 命周期函数--监听页面加载
onLoad:function(options){
//更新图片路径地址提示图
url='/images/'+options.level
//更新提示图的地址
this.setData({url:url})
//创建画布上下文
this.ctx=wx.createCanvasContext('myCanvas')
//打乱方块顺序
this.shuffle()
//绘制画布内容
this.drawCanvas()
},
})
- 当前效果如图所示:
3.移动被点击的方块。
-
修改game.wxml页面中的画布组件(),为其绑定触摸事件。
-
WXML(pages/game/game.wxml)代码修改后如下:
<!--pages/game/game.wxml--> <view class="container"> <!--提示图区域--> <view class='title'>提示图</view> <image src='{{url}}'></image> <!--游戏画布--> <canvas canvas-id='myCanvas' bindtouchstart='touchBox'></canvas> <!--“重新开始”按钮--> <button type='warn'>重新开始</button> </view>
-
-
在game.js文件添加自定义函数 touchBox,用于实现图片方块的移动
-
对应的JS(pages/game/game.js)代码片段添加如下:
Page{ ... touchBox: function (e) { if (this.data.isWin) { return } var x = e.changedTouches[0].x var y = e.changedTouches[0].y var row = parseInt(y / w) var col = parseInt(x / w) if (num[row][col] != '22') { this.moveBox(row, rol) this.drawCanvas() if (this.isWin()) { let ctx = this.ctx ctx.drawImage(url, 0, 0) ctx.setFillStyle('#e64340') ctx.setTextAlign('center') ctx.setFontSize(60) ctx.fillText('游戏成功', 150, 150) ctx.draw() } } }, moveBox: function (i, j) { if (i > 0) { if (num[i - 1][j] == '22') { num[i - 1][j] = num[i][j] num[i][j] = '22' return } } if (i < 2) { if (num[i + 1][j] == '22') { num[i + 1][j] = num[i][j] num[i][j] = '22' return } } if (j > 0) { if (num[i][j - 1] == '22') { num[i][j - 1] = num[i][j] num[i][j] = '22' return } } if (j < 2) { if (num[i][j + 1] == '22') { num[i][j + 1] = num[i][j] num[i][j] = '22' return } } }, ... }
-
- 当前效果如图所示。
-
(3)判断游戏成功
-
首先在game.js文件中的data中添加初始数据 isWin,用于标记游戏成功与否。
-
对应的JS(pages/game/game.js)代码片段添加如下:
Page({ /** * 页面的初始数据 */ data: { isWin:false },
-
-
在上述代码中,false表示未成功,当成功时会重置为True。在game.js文件中添加自定义函数isWin,用于判断函数是否已经成功。
-
JS代码对应片段添加如下:
Page({ isWin:function(){ for(var i=0;i<3;i++){ for(var j=0;j<3;j++){ if(num[i][j]!=i*10+j){ return false } } } this.setData({isWin:true}) return true }, })
-
-
然后修改game.js中的touchBox函数,要求被触发时追加对游戏成功状态的判断。
-
JS代码对应片段修改如下:
touchBox: function (e) { if (this.data.isWin) { return } var x = e.changedTouches[0].x var y = e.changedTouches[0].y var row = parseInt(y / w) var col = parseInt(x / w) if (num[row][col] != '22') { this.moveBox(row, col) this.drawCanvas() if (this.isWin()) { let ctx = this.ctx ctx.drawImage(url, 0, 0) ctx.setFillStyle('#e64340') ctx.setTextAlign('center') ctx.setFontSize(60) ctx.fillText('游戏成功', 150, 150) ctx.draw() } } },
可得成功后的效果如图所示:
-
(4)重新开始游戏
-
修改game.wxml代码,为“重新开始”按钮追加自定义函数的点击事件。
-
WXML(pages/game/game.wxml)代码片段修改如下:
<!--pages/game/game.wxml--> <view class="container"> <!--提示图区域--> <view class='title'>提示图</view> <image src='{{url}}'></image> <!--游戏画布--> <canvas canvas-id='myCanvas' bindtouchstart='touchBox'></canvas> <!--“重新开始”按钮--> <button type='warn' bindtap='restartGame'>重新开始</button> </view>
-
-
在game.js文件中添加 restartGame函数,用于重新开始游戏。
-
对应的JS(pages/game/game.js)代码片段添加如下:
Page({ restartGame:function(){ this.seaData({ isWin:false }) this.shuffle() this.drawCanvas }, })
-
此时页面效果如图所示。
1.
2. 点击**“重新开始”**后如图所示:
3.
三、程序运行结果
列出程序的最终运行结果及截图。
四、问题总结与体会
一.问题
实验中代码相对较多,各种符号以及代码字幕容易出现问题,经常出现运行不出或者代码无法成功,检查发现可能是细节错误。需要更加细心
二.总结与体会
- 小游戏极其有趣,学习到了很多相关js以及各种的知识,而且更加了解了相关代码的含义,且对于各种组件进行了进一步的理解,收获满满。
- 除此之外,设计出的拼图小游戏很有意思,可以一直玩,有趣也充满了成就感。