2024年夏季《移动软件开发》实验报告
姓名和学号? | 林佳欣,22020007054 |
---|---|
本实验属于哪门课程? | 中国海洋大学24夏《移动软件开发》 |
实验名称? | 实验6:推箱子游戏 |
博客地址? | LinJason.OUC-CSDN博客 |
Github仓库地址? | WechatMiniProgram: 2024年中国海洋大学移动软件开发作业 (gitee.com) |
(备注:将实验报告发布在博客、代码公开至 github 是 加分项,不是必须做的)
一、实验目标
1、综合所学知识创建完整的推箱子游戏;2、能够在开发过程中熟练掌握真机预览、调试等操作。
二、实验步骤
列出实验的关键步骤、代码解析、截图。
1、导航栏设计
在app.json中输入代码
"window": {
"navigationBarTitleText": "推箱子游戏",
"navigationBarBackgroundColor": "#E64340"
},
//其它代码
2、公共逻辑:游戏地图数据设计
在utils/data.js中写入代码,在后续的game的页面中需要显示地图
//0:墙的外围;1:墙;2:路;3:终点;4:箱子;5:人物
//关卡1
var map1 = [
[0,1,1,1,1,1,0,0],
[0,1,2,2,1,1,1,0],
[0,1,5,4,2,2,1,0],
[1,1,1,2,1,2,1,1],
[1,3,1,2,1,2,2,1],
[1,3,4,2,2,1,2,1],
[1,3,2,2,2,4,2,1],
[1,1,1,1,1,1,1,1]
]
//关卡2
var map2 = [
[0,0,1,1,1,0,0,0],
[0,0,1,3,1,0,0,0],
[0,0,1,2,1,1,1,1],
[1,1,1,4,2,4,3,1],
[1,3,2,4,5,1,1,1],
[1,1,1,1,4,1,0,0],
[0,0,0,1,3,1,0,0],
[0,0,0,1,1,1,0,0]
]
//关卡3
var map3 = [
[0,0,1,1,1,1,0,0],
[0,0,1,3,3,1,0,0],
[0,1,1,2,3,1,1,0],
[0,1,2,2,4,3,1,0],
[1,1,2,2,5,4,1,1],
[1,2,2,1,4,4,2,1],
[1,2,2,2,2,2,2,1],
[1,1,1,1,1,1,1,1]
]
//关卡4
var map4 = [
[0,1,1,1,1,1,1,0],
[0,1,3,2,3,3,1,0],
[0,1,3,2,4,3,1,0],
[1,1,1,2,2,4,1,1],
[1,2,4,2,2,4,2,1],
[1,2,1,4,1,1,2,1],
[1,2,2,2,5,2,2,1],
[1,1,1,1,1,1,1,1]
]
module.exports={
maps:[map1,map2,map3,map4]
}
3、首页设计
在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='{{index}}'>
<image src="/images/{{item}}"></image>
<text>第{{index+1}}关</text>
</view>
</view>
</view>
在index.wxss中输入代码,设计首页游戏选关的布局和图片摆设
/*关卡列表区域*/
.levelBox{
width:100%;
}
/*单个关卡区域*/
.box {
width:50%;
float: left;
margin:20rpx 0;
display:flex;
flex-direction:column;
align-items:center;
}
/*选关图片*/
image {
width:300rpx;
height:300rpx
}
在index.js中写入代码,实现首页的选择关卡逻辑
Page({
//设置四张地图的图片
data: {
levels:[
'level01.png',
'level02.png',
'level03.png',
'level04.png',
]
},
//通过点击图片,跳转至game页面,并将level属性传递到game页面
chooseLevel:function(e){
let level=e.currentTarget.dataset.level
wx.navigateTo({
url: '../game/game?level='+level,
})
},
/*其它生命周期函数*/
onLoad: function (options) {},
onReady: function () {},
onShow: function () {},
onHide: function () {},
onUnload: function () {},
onPullDownRefresh: function () {},
onReachBottom: function () {},
onShareAppMessage: function () {}
})
4、游戏界面设计
在game.wxml中设计上下左右的按钮,以及重新开始的按钮选项
<view class='container'>
<!--关卡提示-->
<view class='title'>第{{level}}关</view>
<!--游戏画布-->
<canvas canvas-id ='myCanvas'></canvas>
<!--方向键-->
<view class = 'btnBox'>
<button type='warn'bindtap='up'>↑</button>
<view>
<button type="warn" bindtap='left'>←</button>
<button type='warn'bindtap='down'>↓</button>
<button type='warn'bindtap='right'>→</button>
</view>
</view>
<!--“重新开始”按钮-->
<button type = 'warn'bindtap='restartGame'>重新开始</button>
</view>
在game.wxss中设计游戏的画布、方向键按钮第一行和第二回的样式以及所有按钮的样式
/*游戏画布样式*/
canvas{
border:1rpx solid;
width:320px;
height:320px;
}
.btnBox{
display:flex;
flex-direction:column;
align-items:center;
}
/*方向键按钮第二行*/
.btnBox view{
display:flex;
flex-direction:row;
}
/*所有方向键按钮*/
.btnBox button{
width:90rpx;
height:90rpx;
}
/*所有按钮样式*/
button{
margin:10rpx;
}
在game.js中写入代码,完成方向键的逻辑实现,以及胜负判断和重新开始的功能
//调用utils/data.js
var data=require('../../utils/data.js')
var map = [
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0]
]
var box = [
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0]
]
var w =40
var row=0
var col=0
Page({
data: {
},
initMap: function(level) {
//读取原始的游戏地图数据
let mapData = data.maps[level]
//使用双重for循环记录地图数据
for (var i=0;i<8;i++){
for (var j=0;j<8;j++){
box[i][j]= 0
map[i][j] = mapData[i][j]
if (mapData[i][j]== 4){
box[i][j]= 4
map[i][j]= 2
} else if (mapData[i][j]== 5) {
map[i][j] = 2
//记录小鸟的当前行和列
row = i
col = j
}
}
}
},
drawCanvas: function(){
let ctx = this.ctx
//清空画布
ctx.clearRect(0,0,320,320)
//使用双重for循环绘制8x8的地图
for (var i=0;i<8;i++){
for (var j=0;j<8;j++){
//默认是道路
let img = 'ice'
if (map[i][j]== 1){
img = 'stone'
} else if (map[i][j]==3){
img = 'pig'
}
//绘制地图
ctx.drawImage('/images/icons/' + img + '.png',j*w,i* w,w,w)
if (box[i][j]== 4){
//叠加绘制箱子
ctx. drawImage('/images/icons/box.png',j* w,i*w,w,w)
}
}
}
//叠加绘制小鸟
ctx.drawImage('/images/icons/bird.png', col*w,row*w,w,w)
ctx.draw()
},
up:function(){
//不在最顶端才考虑上移
if (row> 0){
//如果上方不是墙或箱子,可以移动小鸟
if (map[row - 1][col]!= 1 && box[row - 1][col]!= 4){
//更新当前小鸟的坐标
row = row-1
//如果上方是箱子
}
else if (box[row - 1][col]== 4){
//箱子不在最顶端才能考虑推动
if (row - 1>0){
//如果箱子上方不是墙或箱子
if (map[row - 2][col] != 1 && box[row - 2][col]!= 4){
box[row - 2][col] = 4
box[row - 1][col]= 0
//更新当前小鸟的坐标
row = row -1
}
}
}
//重新绘制地图
this. drawCanvas()
this.checkWin()
}
},
down: function(){
//不在最底端才考虑下移
if (row<7){
//如果下方不是墙或箱子,可以移动小鸟
if (map[row+1][col]!= 1 && box[row+1][col]!=4) {
//更新当前小鸟的坐标
row = row+1
}
//如果下方是箱子
else if (box[row+1][col] == 4){
//箱子不在最底端才能考虑推动
if (row + 1<7){
//如果箱子下方不是墙或箱子
if (map[row+2][col]!= 1 && box[row +2][col]!= 4){
box[row+2][col] = 4
box[row+1][col] = 0
//更新当前小鸟的坐标
row = row+ 1
}
}
}
//重新绘制地图
this.drawCanvas()
this.checkWin()
}
},
left: function(){
//不在最左侧才考虑左移
if (col > 0){
//如果左侧不是墙或箱子,可以移动小鸟
if (map[row] [col-1]!= 1 && box[ row] [col - 1]!= 4){
//更新当前小鸟的坐标
col= col-1
}
//如果左侧是箱子
else if (box[row][col-1]==4){
//箱子不在最左侧才能考虑推动
if (col - 1>0){
//如果箱子左侧不是墙或箱子
if (map[row][col - 2]!= 1 && box[row][col - 2]!= 4){
box[row] [col- 2]=4
box[row][col- 1]=0
//更新当前小鸟的坐标
col= col- 1
}
}
}
//重新绘制地图
this.drawCanvas()
this.checkWin()
}
},
right: function(){
//不在最右侧才考虑右移
if (col<7){
//如果右侧不是墙或箱子,可以移动小鸟
if (map[row] [col+1]!= 1 && box[row] [col+1]!= 4){
//更新当前小鸟的坐标
col= col+1
}
//如果右侧是箱子
else if (box[row][col+1]==4){
//箱子不在最右侧才能考虑推动
if (col+1<7){
//如果箱子右侧不是墙或箱子
if (map[row][col+2]!= 1 && box[row][col+2]!= 4){
box[row] [col+2]=4
box[row][col+1]=0
//更新当前小鸟的坐标
col= col+1
}
}
}
//重新绘制地图
this.drawCanvas()
this.checkWin()
}
},
isWin: function(){
//使用双重for 循环遍历整个数组
for (var i = 0; i<8; i++){
for (var j = 0;j<8;j++){
//如果有箱子没在终点
if (box[i][j] == 4 && map[i][j]!= 3){
//返回 false,表示游戏尚未成功
return false
}
}
}
//返回true,表示游戏成功
return true
},
//调用isWin函数判断是否成功
checkWin: function(){
if (this.isWin()){
wx. showModal({
title:'恭喜',
content:'游戏成功!',
showCancel:false
})
}
},
//重新开始
restartGame:function(){
this.initMap(this.data.level-1)//重新初始化地图数据
this.drawCanvas()//重新绘制地图
},
//接受首先传来的关卡序号,并生成对应的画布和关卡地图
onLoad(options) {
let level=options.level
this.setData({
level:parseInt(level)+1
})
this.ctx=wx.createCanvasContext('myCanvas')
this.initMap(level)
this.drawCanvas()
},
//其它生命周期函数
onReady() {},
onShow() {},
onHide() {},
onUnload() {},
onPullDownRefresh() {},
onReachBottom() {},
onShareAppMessage() {}
})
三、程序运行结果
列出程序的最终运行结果及截图。
四、问题总结与体会
描述实验过程中所遇到的问题,以及是如何解决的。有哪些收获和体会,对于课程的安排有哪些建议。
问题:按键上左右都能触发,但是下触发不了
解决方法:将上的按键,改为向下的函数发现能够运行,因此判断是向下的按键出现问题,经过检查发现是bindtap
输入错了
收获和体会:通过这次的实验,我学会了如何利用小程序制作游戏,且能够以图片元素的变化来生动展现原本是0、1、2、3、4、5的数据,其中最重要的就是地图数据的处理和绘制,以及能否上下左右的判定,需要考虑是否旁边是墙还是箱子;如果是箱子,那么箱子后面是否是墙诸如此类的判定都需要有严格的逻辑;此外我也巩固了首页到另一个页面传参的操作和调用公用数据出口的操作。