小程序功能
- 首页呈现关卡菜单,玩家可以自由选择4个游戏关卡
- 游戏关卡页面可以通过点击上下左右4个按钮实现小鸟的移动
- 在游戏关卡页面游戏成功后会弹出提示框,并且可以选择重新开始游戏
注:游戏图片下载链接:https://gaopursuit.oss-cnbeijing.aliyuncs.com/course/mobileDev/boxgame_images.zip
一、实验目标
1、综合所学知识创建完整的推箱子游戏;
2、能够在开发过程中熟练掌握真机预览、调试等操作。
二、实验步骤
1.创建项目并配置页面文件
- 在
pages
文件夹中创建index
和game
两个页面 - 新建
images
和utils
文件夹, 分别存储游戏图片素材和和公共data.js
文件
效果如下:
2.导航栏设计
- 在
windows
属性中自定义导航栏效果,设置背景色为珊瑚红色、字体为白色
app.json
文件的代码如下:
"window": {
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#E64340",
"navigationBarTitleText": "推箱子游戏",
"navigationBarTextStyle":"white"
},
效果如下:
3.首页设计
(1)公共样式设计
- 设置页面容器和顶端标题的公共样式
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;
}
(2)首页设计
- 使用
<view>
组件设置顶端标题 - 使用数组循环显示关卡列表
index.wxml
文件代码如下:
<view class="container">
<!--标题-->
<view class="title">游戏选关</view>
<!--关卡列表-->
<view class='levelBox'>
<view class="box">
<image src="/images/level01.png" />
<text>第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;
}
效果如下:
4.游戏页设计
- 游戏页面包括游戏关卡标题、游戏画面、方向键和重新开始按钮
game.wxml
文件代码如下:
<!--game.wxml-->
<view class='container'>
<!-- 关卡提示 -->
<view class="title">第1关</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'>重新开始</button>
</view>
game.wxss
文件代码如下:
/**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;
}
效果如下:
5.公共逻辑
- 在
data.js
文件中配置游戏地图数据,需要存放4个二维数组(代表4个不关卡的地图数据) - 接着在
data.js
文件中使用module.exports
语句暴露数据接口 - 最后在
game.js
文件顶端引用公共JS
文件
data.js
文件代码如下:
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],
]
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],
]
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],
]
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]
}
game.js
文件引用代码如下:
var data = require('../../utils/data.js')
6.首页逻辑
(1)展示关卡列表
- 在
data.js
文件中添加关卡图片的数据信息,并在index.wxml
文件中显示出来
index.js
文件代码如下:
Page({
/**
* 页面的初始数据
*/
data: {
levels: [
'level01.png',
'level02.png',
'level03.png',
'level04.png'
]
},
})
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}}" />
<text>第{{index+1}}关</text>
</view>
</view>
</view>
效果如下:
(2)点击跳转游戏页面
- 首先在
index.wxml
文件中为关卡列表项目添加点击事件 - 在
index.js
文件中添加chooseLevel
函数
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}}" />
<text>第{{index+1}}关</text>
</view>
</view>
</view>
index.js
文件代码如下:
Page({
/**
* 页面的初始数据
*/
data: {
levels: [
'level01.png',
'level02.png',
'level03.png',
'level04.png'
]
},
/**
* 自定义函数--游戏选关
*/
chooseLevel:function(e){
let level = e.currentTarget.dataset.level
wx.navigateTo({
url:'../game/game?level='+level
})
},
})
7.游戏页逻辑
(1)显示当前第几关
- 在游戏页面接收关卡信息,并显示对应的图片内容
game.js
文件代码如下:
Page({
/**
* 页面的初始数据
*/
data: {
level: 1
},
// 生命周期函数
onLoad:function(options) {
let level=options.level
this.setData({
level:parseInt(level)+1
})
},
})
效果如下:
(2)游戏逻辑实现
- 首先在
game.js
文件中添加游戏初始数据信息 - 初始化游戏页面
- 在
game.js
文件中添加initMap
函数,初始化游戏地图数据 - 在
game.js
中添加drawCanvas
函数,将地图信息会绘制到画布上 - 在
game.js
文件的onLoad
函数中创建画布上下文,并调用initMap
函数和drawCanvas
函数
- 在
game.js
文件代码如下:
// pages/game/game.js
var data = require('../../utils/data.js')
var level
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: {
level: 1
},
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循环绘制8*8地图
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()
},
// 生命周期函数
onLoad:function(options) {
let level=options.level
this.setData({
level:parseInt(level)+1
})
this.ctx=wx.createCanvasContext('myCanvas')
this.initMap(level)
this.drawCanvas()
},
})
效果如下:
(3)方向键逻辑实现
- 为
game.wxml
文件中的4个方向键绑定点击事件 - 在
game.js
文件中添加up
、down
、left
、right
函数,实现小鸟上下左右4个方向的移动
game.wxml
文件代码如下:
<!--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.js
文件代码如下:
// 方向键
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()
}
},
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()
}
},
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()
}
},
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.checkWin()
}
},
效果如下:
(移动前)
(移动后)
8.判断游戏成功
- 在
game.js
文件中添加isWin
函数,判断游戏是否已经成功 - 然后在
game.js
中添加checkWin
函数,游戏成功就弹出提示框 - 最后在
game.js
的4个方向函数中添加判断游戏是否成功的函数,以up
函数为例,其余三个函数均相同
isWin
函数代码如下:
isWin:function(){
for(var i=0;i<8;i++){
for(var j=0;j<8;j++){
if(box[i][j]==4&&map[i][j]!=3){
return false
}
}
}
return true
},
checkWin
函数代码如下:
checkWin:function(){
if(this.isWin()){
wx.showModal({
title:'恭喜',
content:'游戏成功',
showCancel:false
})
}
},
up
函数添加代码如下:
//检查游戏是否成功
this.checkWin()
效果如下:
9.重新开始游戏
- 在
game.wxml
中给“重新开始”按钮添加点击事件,并在game.js
文件中添加restartGame
函数
game.wxml
文件代码如下:
<!-- 重新开始按钮 -->
<button type='warn' bindtap='restartGame'>重新开始</button>
game.js
文件中restartGame
函数代码如下:
restartGame:function(){
this.initMap(this.data.level-1)
this.drawCanvas()
},
效果如下:
(重新开始前)
(重新开始后)
三、程序运行结果
(首页)
(选择关卡后)
(小鸟移动页面)
(重新开始页面)
(成功页面)
四、问题总结与体会
在游戏设计过程中,我学会了如何分析问题、制定解决方案,提高了自己的逻辑思维能力;本次实验让我在短时间内掌握了推箱子游戏小程序的开发过程,提高了自己的编程实践能力。在今后的学习和工作中,我会继续努力,不断积累经验,为成为一名优秀的程序员而努力。