掌握<canvas>组件和绘图API

本项目一共需要两个页面:首页和游戏页面。首页用于呈现关卡菜单,点击对应难度的关卡后进入游戏画面。

首页功能需求:

(1)包含标题和关卡列表

(2)关卡至少要有6个关卡选项,每个关卡显示预览图片和第几关

(3)点击关卡列表可以打开对应的游戏界面

游戏页功能需求:

(1)游戏页面需要显示游戏提示图,游戏画面,“重新开始”按钮

(2) 每关游戏提示图显示对应的图片预览

(3)游戏画面随机将原图打乱为3x3的小方块,并且可移动被点击的方块。

(4)点击“重新开始”按钮可以重新随机打乱小方块并开始游戏

创建页面文件

需创建index首页页面和game游戏页面

页面设计

app.json

{"pages":["pages/index/index"],"window": {"navigationBarBackgroundColor": "#E64344","navigationBarTitleText": "拼图游戏"}
}

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;
}

index.wxml

<!--index.wxml-->
<view class="container">
<!-- 标题 -->
<view class="title"> 游戏选关 </view>
<!-- 关卡列表 -->
<view class="levelBox"><view class="box"><image src="/images/p1.jpg"></image><text>第一关</text></view>
</view>
</view>

index.wxss

/**index.wxss**/
/* 关卡列表区域 */
.levelBox{width: 100%;
}
/* 单个关卡区域 */
.box{width: 50%;float: left;margin: 25rpx 0;display: flex;flex-direction: column;align-items: center;
}
/* 选关图片 */
image{width: 260rpx;height: 260rpx;
}

游戏页面设计

游戏页面需要用户点击首页的关卡列表,然后在新窗口中打开该页面。游戏页面包括游戏提示图,游戏画面和“重新开始”按钮

由于暂时没有做点击跳转的逻辑设计,可以在开发工具顶端选择“普通编译”下的“添加编译模式”,并携带临时测试参数level=pi1.jpg


游戏页面设计

<!--pages/game/game.wxml--><view class="container">
<!-- 提示图区域 -->
<view class="title"> 提示图 </view><image src="/images/p1.jpg"></image><!-- 游戏画布 --><canvas canvas-id="myCanvas"></canvas><!-- 重新开始按钮 --><button type="warn">重新开始</button>
</view>
/* pages/game/game.wxss */
/* 提示图样式 */
image{width: 250rpx;height: 250rpx;
}/* 画布样式 */
canvas{border: 1rpx solid;widows: 300rpx;height: 300rpx;
}


逻辑实现

首页逻辑

首页功能主要是展示关卡列表,点击图片能跳转到游戏界面

关卡列表展示

在JS文件的data中录入关卡图片的数据信息。

index.js

 */data: {levels:['p1.jpg','p2.jpg','p3.jpg','p4.jpg','p5.jpg','p6.jpg']},

<view>组件添加wx:for属性循环显示关卡列表数据和图片

index.wxml

<!-- 关卡列表 -->
<view class="levelBox"><view class="box" wx:for="{{levels}}" wx:key="levels{{index}}"><image src="/images/{{item}}"></image><text>第{{index+1}}关</text></view>
</view>

点击跳转游戏页面

若需要用户点击图片即可实现跳转,需要首先为关卡列表项目添加点击事件

index.wxml

<!-- 关卡列表 -->
<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>

为关卡添加了自定义点击事件函数chooseLevel,并且使用data-level属性携带了关卡图片信息

chooseLevel:function(e){let level = e.currentTarget.dataset.levelwx.navigateTo({url: '../game/game?level='+level,})},

游戏页逻辑

游戏页主要有两个功能需要实现,一是显示提示图,二是游戏逻辑实现

在游戏页接收关卡信息

Page({/*** 生命周期函数--监听页面加载*/onLoad: function (options) {// 更新图片路径地址url='/images/'+options.level// 更新提示图的地址this.setData({urrl:url})},})
<!--pages/game/game.wxml--><view class="container">
<!-- 提示图区域 -->
<view class="title"> 提示图 </view><image src="{{url}}"></image><!-- 游戏画布 --><canvas canvas-id="myCanvas"></canvas><!-- 重新开始按钮 --><button type="warn">重新开始</button>
</view>

游戏逻辑实现

首先在game.js文件的顶端记录一些游戏初始数据信息

// pages/game/game.js
// 方块的初始位置
var num = [['00','01','02'],['10','11','12'],['20','21','22']
]
// 方块的宽度
var w = 100
// 图片的初始地址
var url = '/images/p1.jpg'Page({/*** 页面的初始数据*/

初始化拼图画面

随机抽取画面中的任意两个方块,然后交换彼此位置,在进行足够多次数的交换之后基本可以实现随机打乱效果。

但有时会陷入死局

可以考虑从空白方块的所在位置入手,每次随机让它和周围的邻近方块交换位置,可以通过方块反向移动回到最初状态(确保本局有解),并且在交换足够多次数之后可以实现随机打乱效果

在game.js文件中添加shuffle函数,用于重新开始游戏


Page({// 自定义函数-随机打乱方块顺序shuffle:function(){// 令所有方块回归初始位置num = [['00','01','02'],['10','11','12'],['20','21','22']]// 记录当前空白方块的行和列var row = 2var col = 2// 打乱方块顺序100次for(var i=0;i<100;i++){// 随机产生其中一个方向:上(0),下(1),左(2),右(3)var direction = Math.round(Math.random()*3)//上:0if(direction == 0){// 空白方块不在最上面一行if(row != 0){// 交换位置num[row][col] = num[row-1][col]num[row-1][col] = '22'row -= 1}}//下:1else if(direction == 1){// 空白方块不在最下面一行if(row != 2){// 交换位置num[row][col] = num[row+1][col]num[row+1][col] = '22'// 更新空白块row += 1}}//左:2else if(direction == 2){// 空白方块不在最左侧if(col != 0){// 交换位置num[row][col] = num[row][col-1]num[row][col-1] = '22'// 更新空白块col -= 1}}//右:3else if(direction == 3){// 空白方块不在最右侧if(col != 2){// 交换位置num[row][col] = num[row][col+1]num[row][col+1] = '22'// 更新空白块col += 1}}}},/*** 页面的初始数据*/data: {},

每次使用Math.random()方法从上下左右4个方向中随机产生一个方向,之后如果符合条件则交换空白方块和图片方块位置

在game.js中添加自定义函数drawCanvas,用于打乱后的图片方块绘制到画布上

game.js

// 自定义画布函数drawCanvas:function(){let ctx = this.ctx// 清空画布ctx.clearRect(0,0,300,300)// 使用双重for循环绘制3x3的拼图for(var i = 0; i < 3;i++){for(var j = 0; j < 3;j++){if(num[i][j] != '22'){// 获取行和列var row = parseInt(num[i][j]/10)var col = num[i][j]%10//绘制方块ctx.drawImage(url,col*w,row*w,w,w,j*w,i*w,w,w)}}}ctx.draw()},

最后在game.js的onLoad函数中调用自定义函数shuffle和drawCanvas.

对应的game.js

/*** 生命周期函数--监听页面加载*/onLoad: function (options) {// 更新图片路径地址url='/images/'+options.level// 更新提示图的地址this.setData({urrl:url})// 创建画布上下文this.ctx = wx.createCanvasContext('myCanvas')// 打乱方块顺序this.shuffle()// 绘制画布内容this.drawCanvas()},

移动被点击的块

修改game.wxml页面中的画布组件<canvas>为其绑定触摸事件

 <!-- 游戏画布 --><canvas canvas-id="myCanvas" bindtouchstart="touchBox"></canvas>

在game.js文件中添加自定义函数touchBox,用于实现图片方块的移动

<!-- 游戏画布 --><canvas canvas-id="myCanvas" bindtouchstart="touchBox"></canvas>
// 自定义函数-监听点击方块事件touchBox:function(e){// 如果游戏成功,不做任何操作if(this.data.isWin){// 终止本函数return}// 获取被点击方块的坐标x和yvar x = e.changedTouches[0].xvar y = e.changedTouches[0].y// // 换算成行和列var row = parseInt(y/w) //将坐标除于小方块的宽度w得出行和列var col = parseInt(x/w)// 如果点击的不是空白位置if(num[row][col]!='22'){//尝试移动方块this.moveBox(row,col)}},
/*** 自定义函数--移动方块*/moveBox: function (i,j) {//情况1:如果被点击的方块不在最上方,检查可否上移if(i>0){//如果方块上方是空白的if(num[i-1][j] == '22'){// 交换方块与空白的位置num[i-1][j] = num[i][j]num[i][j] = '22'return}}//情况2:如果被点击的方块不在最下方,检查可否下移if(i<2){//如果方块下方是空白的if(num[i+1][j] == '22'){// 交换方块与空白的位置num[i+1][j] = num[i][j]num[i][j] = '22'return}}//情况3:如果被点击的方块不在最左边,检查可否左移if(j>0){//如果方块上方是空白的if(num[i][j-1] == '22'){// 交换方块与空白的位置num[i][j-1] = num[i][j]num[i][j] = '22'return}}//情况4:如果被点击的方块不在最右边,检查可否右移if(j<2){//如果方块右边是空白的if(num[i][j+1] == '22'){// 交换方块与空白的位置num[i][j+1] = num[i][j]num[i][j] = '22'return}}},

判断游戏成功

首先在game.js文件的data中添加初始数据isWin,用于标记游戏成功与否。

 /*** 页面的初始数据*/data: {isWin:false},

isWin为false表示游戏尚未成功,当成功时会重置为true.

在game.js文件中添加自定义函数isWin,用于判断游戏是否已经成功。

//自定义函数 - 判断游戏是否成功isWin:function(){// 使用双重for循环遍历整个数组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})// 返回i真,游戏成功return true},
 // 自定义函数-监听点击方块事件touchBox:function(e){// 如果游戏成功,不做任何操作if(this.data.isWin){// 终止本函数return}

修改game.js的touchBox函数,要求被触发时追加对游戏成功状态的判断

// 自定义函数-监听点击方块事件touchBox:function(e){// 如果游戏成功,不做任何操作if(this.data.isWin){// 终止本函数return}// 获取被点击方块的坐标x和y。。。略// 如果点击的不是空白位置。。。略// 重新绘制画布内容this.drawCanvas()// 判断游戏是否成功if(this.isWin()){// 在画面上绘制提示语句let ctx =this.ctx// 绘制完整图片ctx.drawImage(url,0,0)// 绘制文字ctx.setFillStyle('#e64340')//调节文字颜色ctx.setTextAlign('center') //表示这个字在这个坐标点上居中显示ctx.setFontSize(50) //调节字的大小ctx.fillText('游戏成功',150,150)ctx.draw()}}},

重新开始游戏

修改game.wxml代码,为“重新开始”按钮追加定义

<!-- 重新开始按钮 --><button type="warn" bindtap="restartGame">重新开始</button>

在game.js文件中添加restartGame函数,用于重新开始游戏

// 自定义函数-重新开始游戏restartGame:function(){// 更新游戏成功状态this.setData({isWin:false})// 打乱方块顺序this.shuffle()//绘制画布内容this.drawCanvas()},

完整代码:

app.json

{"pages":["pages/index/index","pages/game/game"],"window": {"navigationBarBackgroundColor": "#E64344","navigationBarTitleText": "拼图游戏"}
}

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;
}

首页代码展示:

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="chooseLevel" data-level='{{item}}'><image src="/images/{{item}}"></image><text>第{{index+1}}关</text></view>
</view>
</view>

index.wxss

/**index.wxss**/
/* 关卡列表区域 */
.levelBox{width: 100%;
}
/* 单个关卡区域 */
.box{width: 50%;float: left;margin: 25rpx 0;display: flex;flex-direction: column;align-items: center;
}
/* 选关图片 */
image{width: 260rpx;height: 260rpx;
}

index.js

// index.js
// 获取应用实例
Page({/*** 页面的初始数据*/data: {levels:['p1.jpg','p2.jpg','p3.jpg','p4.jpg','p5.jpg','p6.jpg']},chooseLevel:function(e){let level = e.currentTarget.dataset.levelwx.navigateTo({url: '../game/game?level='+level,})},})

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.wxss

/* pages/game/game.wxss */
/* 提示图样式 */
image{width: 250rpx;height: 250rpx;
}/* 画布样式 */
canvas{border: 1rpx solid;width: 300px;height: 300px;
}

game.js

// pages/game/game.js
// 方块的初始位置
var num = [['00','01','02'],['10','11','12'],['20','21','22']
]
// 方块的宽度
var w = 100
// 图片的初始地址
var url = '/images/p1.jpg'Page({/*** 页面的初始数据*/data: {isWin:false},// 自定义画布函数drawCanvas:function(){let ctx = this.ctx// 清空画布ctx.clearRect(0,0,300,300)// 使用双重for循环绘制3x3的拼图for(var i = 0; i < 3;i++){for(var j = 0; j < 3;j++){if(num[i][j] != '22'){// 获取行和列var row = parseInt(num[i][j]/10)var col =  parseInt(num[i][j]%10)//绘制方块ctx.drawImage(url,col*w,row*w,w,w,j*w,i*w,w,w)}}}ctx.draw()},// 自定义函数-随机打乱方块顺序shuffle:function(){// 令所有方块回归初始位置num = [['00','01','02'],['10','11','12'],['20','21','22']]// 记录当前空白方块的行和列var row = 2var col = 2// 打乱方块顺序100次for(var i=0;i<100;i++){// 随机产生其中一个方向:上(0),下(1),左(2),右(3)var direction = Math.round(Math.random()*3)//上:0if(direction == 0){// 空白方块不在最上面一行if(row != 0){// 交换位置num[row][col] = num[row-1][col]num[row-1][col] = '22'row -= 1}}//下:1else if(direction == 1){// 空白方块不在最下面一行if(row != 2){// 交换位置num[row][col] = num[row+1][col]num[row+1][col] = '22'// 更新空白块row += 1}}//左:2else if(direction == 2){// 空白方块不在最左侧if(col != 0){// 交换位置num[row][col] = num[row][col-1]num[row][col-1] = '22'// 更新空白块col -= 1}}//右:3else if(direction == 3){// 空白方块不在最右侧if(col != 2){// 交换位置num[row][col] = num[row][col+1]num[row][col+1] = '22'// 更新空白块col += 1}}}},// 自定义函数-监听点击方块事件touchBox:function(e){// 如果游戏成功,不做任何操作if(this.data.isWin){// 终止本函数return}// 获取被点击方块的坐标x和yvar x = e.changedTouches[0].xvar y = e.changedTouches[0].y// // 换算成行和列var row = parseInt(y/w) //将坐标除于小方块的宽度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(50) //调节字的大小ctx.fillText('游戏成功',150,150)ctx.draw()}}},//自定义函数 - 判断游戏是否成功isWin:function(){// 使用双重for循环遍历整个数组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})// 返回i真,游戏成功return true},// 自定义函数-重新开始游戏restartGame:function(){// 更新游戏成功状态this.setData({isWin:false})// 打乱方块顺序this.shuffle()//绘制画布内容this.drawCanvas()},/*** 自定义函数--移动方块*/moveBox: function (i,j) {//情况1:如果被点击的方块不在最上方,检查可否上移if(i>0){//如果方块上方是空白的if(num[i-1][j] == '22'){// 交换方块与空白的位置num[i-1][j] = num[i][j]num[i][j] = '22'return}}//情况2:如果被点击的方块不在最下方,检查可否下移if(i<2){//如果方块下方是空白的if(num[i+1][j] == '22'){// 交换方块与空白的位置num[i+1][j] = num[i][j]num[i][j] = '22'return}}//情况3:如果被点击的方块不在最左边,检查可否左移if(j>0){//如果方块上方是空白的if(num[i][j-1] == '22'){// 交换方块与空白的位置num[i][j-1] = num[i][j]num[i][j] = '22'return}}//情况4:如果被点击的方块不在最右边,检查可否右移if(j<2){//如果方块右边是空白的if(num[i][j+1] == '22'){// 交换方块与空白的位置num[i][j+1] = num[i][j]num[i][j] = '22'return}}},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {// 更新图片路径地址url='/images/'+options.level// 更新提示图的地址this.setData({url:url})// 创建画布上下文this.ctx = wx.createCanvasContext('myCanvas')// 打乱方块顺序this.shuffle()// 绘制画布内容this.drawCanvas()},
})

拼图游戏-小程序游戏相关推荐

  1. (第39册)《微信小程序游戏开发快速入门到实战》夏敏捷著

    本书是微信小程序游戏开发的入门教程,通过大量案例介绍微信小程序游戏开发的基础知识和技巧.全书分三篇,基础篇对微信小程序的框架文件.微信小程序逻辑层和视图层.微信小程序组件进行详细介绍,包括JavaSc ...

  2. 由于找不到appvisvsubsystems32.dll_找茬游戏大全:我找东西贼快!小清新找茬游戏小程序,点开既玩...

    50000+游戏爱好者已加入我们! 每天推荐好玩游戏! 关注我们,沐沐带你发现好游戏! <我找东西贼快>游戏小程序好玩吗? <我找东西贼快>小游戏怎么玩? 怎么进入<我找 ...

  3. 地图标识符号大全_创意游戏小程序大全:胡建土楼游戏!带你领略不一样的创意小游戏...

    50000+游戏爱好者已加入我们! 每天推荐好玩游戏! 关注我们,沐沐带你发现好游戏! <胡建土楼>游戏小程序好玩吗? <胡建土楼>小游戏怎么玩? 怎么进入<胡建土楼&g ...

  4. java打字小游戏_java实现打字游戏小程序

    本文实例为大家分享了java实现打字游戏小程序的具体代码,供大家参考,具体内容如下 一.设计思路 1.创建一个窗体 2.在窗体上放置一个面板,用paint方法画出英文字母,随机放置字母位置,并随时间自 ...

  5. 如何把小程序游戏运行到自有App中?(IOS篇)

    千呼万唤始出来!FinClip 终于支持小游戏了. 我们团队算是 FinClip 的老用户了,年初就向官方提出了希望 FinClip 支持微信小游戏的建议.随着前段时间 "羊了个羊" ...

  6. 如何把小程序游戏运行到自有app中?

    千呼万唤始出来!FinClip终于支持小游戏了. 我们团队算是FinClip的老用户了,年初就向官方提出了希望FinClip 支持微信小游戏的建议.随着前段时间"羊了个羊"微信小游 ...

  7. 微信小程序游戏「跳一跳」高分秘籍

    饱受争议的微信小程序从今年1月份上线到现在已经快一年了,被寄予厚望的小程序在这一年却并没有达到曾经预期的高度,反而是一直不温不火. 曾经有现象级的刷屏的小程序:匿名聊聊,本以为这会是小程序的新玩法.爆 ...

  8. 2022最新微信小程序游戏:一起来找茬

    正文: 2022最新微信小程序游戏:一起来找茬,有需要的自行去体验吧,其它的就没什么好介绍的了,程序是完整的. 程序: wwwsu.lanzouw.com/iVEie0bm8l5i 图片:

  9. 游戏小程序有哪些?这3个小程序值得推荐!

    游戏是日常生活中的一部分,玩游戏是大多数的兴趣爱好,那么游戏小程序有哪些呢?下面小编为你推荐3个小程序,一起来看看吧! 1.经典黄金矿工 经典黄金矿工是一个抓黄金的游戏小程序,以前我们我们在电脑上玩这 ...

最新文章

  1. python统计小说人物_Python数据分析之基情的择天记
  2. 联泰集群发布水晶系列工作站,用于深度学习场景
  3. 人工智能:技术本无罪,善恶在人心
  4. 地铁线路辅助绘图设计---不做简单的画图师
  5. 使用Windows Server Backup备份恢复Exchange Server 2010数据库
  6. 爬取CSDN最新月份所写的文章的最高阅读量文章(以及统计整个月所写的文章的阅读量的累积和)
  7. OpenCASCADE绘制测试线束:形状修复命令之一般命令
  8. 10.Kong入门与实战 基于Nginx和OpenResty的云原生微服务网关 --- 内置插件
  9. 公安部计算机信息安全产品质量监督检验中心的质量责任和权限
  10. educoder实训平台linux,educoder平台hadoop开发环境搭建[技术学习]
  11. 电话机器人图文+源码介绍
  12. ubuntu linux 软件安装位置,ubuntu查看软件安装位置
  13. python中rgb颜色_自定义RGB颜色与Python诅咒
  14. mysql杀掉sql语句,Mysql使用kill命令解决死锁问题(杀死某条正在执行的sql语句)
  15. J2ME平台的的RPG游戏开发历程(1)-盘古开天辟地,j2me创造游戏世界
  16. PS怎么快速把多个图层形状以中心点缩放
  17. kibana启动报错Error: Could not close browser client handle!
  18. 不是华为手机可以装鸿蒙,不是华为手机,也能用上鸿蒙系统
  19. 【语音合成】基于matlab线性预测共振峰检测和基音参数语音合成【含Matlab源码 562期】
  20. 有关阿里云对SaaS行业的思考,看这一篇就够了

热门文章

  1. 怎样开启成功的“数据分析师”职业生涯(R、Python、机器学习、通信和数据可视化、数据直觉)
  2. java 线框图_你真的搞懂什么是线框图,什么是原型图了吗?
  3. 二维数组的四种创建方法
  4. 信息时代的眼睛保护色
  5. 【Language model】使用RNN LSTM训练语言模型 写出45°角仰望星空的文章
  6. python存储JSON
  7. 你给员工吃草,还指望他们有狼性?
  8. 如何让多个word文档合并成一个
  9. 易烊千玺领衔,潮流自拍手机华为nova5系列发布
  10. Dremel made simple with Parquet