一、实验目标
1、综合应用所学的知识创建完整的推箱子游戏;2、熟练掌握canvas和绘图 API。
二、实验步骤
1.创建项目
依次输入项目名称、选择目录、APPID等,注意选择不使用云服务。
2.修改添加与删除文件(及恢复文件初始化)
删除logs文件夹、utils文件夹,清空index.js、index.json、index.wxss、app.wxss文件中的内容,删除app.json文件中page属性里的"pages/logs/logs",以及上一行的逗号,新增game
页面,新增images
和utils
文件夹分别用于存放图片资源和公共JS文件。
3.编写文件
(1)编译导航栏
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "推箱子游戏",
"navigationBarBackgroundColor": "#E64340"
},
通过对window属性中内容进行修改,可以达到修改导航栏的作用。例如将原本白底黑字修改为蓝底白字,并修改文字内容。
(2)页面设计
1.首页:在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>
2.游戏:在game.wxml中编写代码
<view class = 'container'>
<!--关卡提示-->
<view class='title'>第{{level}}关</view>
<!--游戏画布-->
<view class="outer">
<canvas canvas-id='myCanvas'></canvas>
</view>
<!--方向键-->
<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>
(3)页面元素的整理
1.全局样式:在app.wxss中编写代码
.container{
height: 100vh;
color: #E64340;
font-weight: bold;
display: flex;
flex-direction: column;
justify-content: space-evenly;
}
.title{
font-size: 18pt;
text-align: center;
}
2.首页:在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;
}
.title{
text-align: center;
}
3.游戏:在game.wxss中编写代码。
/*游戏画布样式*/
canvas{
border: 1rpx solid;
width:320px;
height:320px;
}
.outer{
padding: 25px;
}
/*方向键按钮整体区域*/
.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;
}
(4)页面逻辑的实现
1.公共逻辑:在data.js中修改。
//========================================
//地图数据map1~map4
//地图数据:1为墙、2为路、3为终点、4为箱子、5为人物、0为墙的外围
//========================================
//关卡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]
}
2.首页逻辑:在index.js中 修改。
var data = require('../../utils/data.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,
})
}
})
3.游戏逻辑:在game.js中编写代码。
// pages/game/game.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: {
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 循环绘制 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
},
/**
* 自定义函数 -- 游戏成功处理
*/
checkWin: function(){
if (this.isWin()){
wx.showModal({
title:'恭喜',
content:'游戏成功!',
showCancel:false
})
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad:function(options) {
let level=options.level
this.setData({
level:parseInt(level)+1
})
this.ctx=wx.createCanvasContext('myCanvas')
this.initMap(level)
this.drawCanvas()
},
restartGame:function(){
this.initMap(this.data.level-1)
this.drawCanvas()
}
})
三、程序运行结果
运行结果如下:
四、问题总结与体会
这次实验是最后一次实验,也是最复杂的实验。通过这次实验也令我收获了很多,首先第一次接触到微信小程序中画布的概念。同时,对于小程序中实现动态过程也有了一定的了解。