還記得小時(shí)候玩過的經(jīng)典拼圖游戲嗎,上小學(xué)時(shí),在路邊攤用買個(gè)玩具,是一個(gè)正方形盒子形狀,里面裝的是圖片分割成的很多塊,還差一塊,怎么描述好呢,和魔方玩具差不多,有沒有聽說叫二維的魔方,這里用小程序把它實(shí)現(xiàn),有感興趣的同學(xué)可以來看看
準(zhǔn)備
此文章適合新手學(xué)習(xí),使用小程序開發(fā)的讀者閱讀哦
- 會使用微信開發(fā)著工具,或使用HbuilderX工具會做uniapp項(xiàng)目
- 需要熟悉 HTML5 Canvas
- 適合新手入門
開始吧,在電腦上把微信開發(fā)者工具打開,選擇新建項(xiàng)目,最后點(diǎn)確定
- 選擇小程序,再點(diǎn)擊
+
符號新建 - 選擇 使用測試號(沒有自己申請一個(gè))
- 選擇 不使用云服務(wù)
- 選擇模板 JavaScript 基礎(chǔ)模板
新建項(xiàng)目后有生成了一堆東西,不用管它,接下來,將在這基礎(chǔ)上添加代碼
頁面制作
首先,要做的小程序頁面同下面這樣,
二維平面圖片上的塊一開始是打亂的,需要把它轉(zhuǎn)正,到整個(gè)圖片剛好看著沒問題,這個(gè)過程叫拼圖游戲,
移動圖片,拼到正確就算攻關(guān),完成過程時(shí)間越短越厲害,訓(xùn)練大腦,是個(gè)益智游戲
第一個(gè)頁面
第一個(gè)頁面是pages/index/index/wxml
,在里面加了一個(gè)表單form
,還有提交按鈕button form-type="submit"
,點(diǎn)擊開始游戲,相信很多同學(xué)都會自己寫布局,這里不展開講,
在第一個(gè)頁面的pages/index/index.js
里,點(diǎn)擊按鈕事件里寫開始游戲邏輯,就是打開第二個(gè)頁面,很簡單的,這里不展開講,
具體的可以看文章的項(xiàng)目源碼,放在文章結(jié)尾,可以找到,
點(diǎn)開始游戲前,給第二個(gè)頁面?zhèn)魅胗螒蚺渲孟嚓P(guān)的兩個(gè)參數(shù)即可
- 網(wǎng)格列數(shù)
cols
=3 - 選擇圖片
bgImg
=默認(rèn)本地圖片路徑
第二個(gè)頁面
第二個(gè)頁面是pages/game/game.wxml
,布局很簡單,只需要以下幾個(gè)元素標(biāo)簽,其中畫布canvas
標(biāo)簽才是主要的
<view class="content">
<canvas type="2d" id="canvA" class="canvas" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd" ></canvas>
<view class="padding">
<text>?游戲用時(shí):{{timerNum}}s</text>
</view>
<view class="padding">
<button class="btn" size="mini" bindtap="onClick" data-key="help"><icon class="icon" type="info"></icon> <text>游戲說明</text></button>
<button class="btn" size="mini" bindtap="onClick" data-key="restart"><icon class="icon" type="clear"></icon> <text>重新開始</text></button>
</view>
</view>
還有頁面相關(guān)的樣式寫在
pages/game/game.wxss
里,達(dá)到顯示效果
游戲邏輯
初始化
接下來,在pages/game/game.js
寫,實(shí)現(xiàn)畫布canvas
的初始化邏輯,代碼如下
Page({
/**
* 頁面的初始數(shù)據(jù)
*/
data: {
//游戲配置
config:{
cols:3,
bgImg:'/static/1677722908380.jpg'
},
//計(jì)時(shí)數(shù)
timerNum:0,
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面加載
*/
onLoad(options) {
const changel = this.getOpenerEventChannel();
if(changel && changel.once) {
//接收游戲配置參數(shù)
changel.once('args',res=>{
const { cols, bgImgSrc } = res;
this.data.config.cols = cols;
this.data.config.bgImg = bgImgSrc;
this.initCanvas();
})
}else{
this.initCanvas();
}
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面卸載
*/
onUnload() {
//清除所有計(jì)時(shí)器
if(this.data.timer){
clearTimeout(this.data.timer);
}
if(this.data.gameTimer){
clearInterval(this.data.gameTimer);
}
},
/**
* 初始化游戲畫布canvas
*/
initCanvas(){
wx.createSelectorQuery().select('#canvA').fields({ size:true, node:true },res=>{
const { node, width, height } = res;
node.width = width;
node.height = height;
//使用游戲配置
const { cols, bgImg } = this.data.config;
//計(jì)算網(wǎng)格大小和邊距
const gs = Math.trunc(width/cols);
const padding = Math.trunc(width%cols/2);
//設(shè)置畫布相關(guān)的數(shù)據(jù)
this.data.canvas = {
node, width, height, padding,
gridSize: gs,
context: node.getContext('2d'),
};
//異步處理加載圖片
Promise.resolve({
then(resolve,reject) {
let img = node.createImage();
img.onload=function () {
resolve(img)
};
img.onerror=reject;
img.src=bgImg;
}
}).then(res=>{
//分割圖片方法
this.splitImg(res.currentTarget || res);
//重繪方法
this.reDraw();
//開始計(jì)時(shí)
this.data.gameTimer = setInterval(()=> {
if(this.data.isEnd) {
clearInterval(this.data.gameTimer);
this.data.gameTimer=null;
return;
}
this.setData({
timerNum:this.data.timerNum+1
})
},1000)
}).catch(err=>{
console.error(err)
})
}).exec();
},
//...
})
分割圖片
初始化的方法中,還調(diào)用了一些方法,
這里主要講分割圖片splitImg(bgImg)
和重新繪制reDraw(isRest)
方法,展開說明,代碼如下
const AnimationUpdateDelay=10;//更新動畫延遲ms
const MovingPixelsOffset=5;//每次動畫移動的單位距離px
Page({
//...
/**
* 清除畫布
*/
clearBg(){
//...
},
/**
* 分割圖片
* @param String - bgImg 背景圖片元素
*/
splitImg(bgImg){
const { cols } = this.data.config;
const { width, height, context:c, gridSize:gs, padding } = this.data.canvas;
this.clearBg();
//繪制背景圖片
c.drawImage(bgImg,0,0,width,height);
//定義網(wǎng)格集合
const grids = [];
for(let y=0; y<cols; y++){
for(let x=0; x<cols; x++){
const grid = {
//定義網(wǎng)格數(shù)據(jù)...
};
c.rect(grid.left,grid.top,gs,gs);
grids.push(grid);
}
}
//繪制網(wǎng)格
c.stroke();
//定義分割后的圖片集合
let imgs = [];
let lastIndex;
grids.forEach(function (g,i) {
//將部分區(qū)域弄個(gè)圖片集合中...
});
imgs.forEach(function (img,i) {
//將每個(gè)圖片弄到網(wǎng)格集合中...
});
//設(shè)置最后的(空白位置)圖片索引
this.data.lastIndex = lastIndex;
//設(shè)置網(wǎng)格集合
this.data.canvas.grids = grids;
},
/**
* 重繪畫布方法
* @param Boolean - isRest 是否重置
*/
reDraw(isRest){
const { lastIndex } = this.data;
const { width, height, context:c, grids, gridSize:gs } = this.data.canvas;
this.clearBg();
//繪制網(wǎng)格上的圖片
grids.forEach(function (g,i) {
if(isRest) {
//重置網(wǎng)格數(shù)據(jù)...
}
//繪制網(wǎng)格...
});
//繪制出來
c.rect(0,0,width,height);
c.stroke();
},
//...
})
游戲交互
接下來,實(shí)現(xiàn)游戲的交互邏輯,處理用戶點(diǎn)擊某按鈕,
還有,獲取用戶點(diǎn)擊(觸摸)某圖片,再處理下一步,邏輯代碼如下
Page({
//...
/**
* 按鈕點(diǎn)擊事件處理
*/
onClick(e){
//...
},
/**
* 重新開始游戲
*/
reStart(){
//...
},
/**
* 在畫布中開始觸摸事件
*/
onTouchStart(e){
this.data.touch = e.touches[0];
},
/**
* 在畫布中觸摸并移動事件
*/
onTouchMove(e){
this.onTouchStart(e)
},
/**
* 在畫布中不再觸摸時(shí)事件
*/
onTouchEnd(){
const { touch, lastIndex, isAnimation, isEnd } = this.data;
//如果哦沒有觸摸,或在動畫中,或已經(jīng)結(jié)束,就直接返回不處理
if(!touch || isAnimation || isEnd) return;
const { grids, gridSize:gs } = this.data.canvas;
//獲取在畫布觸摸到某圖片的索引
let gIndex = grids.findIndex(function (g) {
//判斷符合條件的某網(wǎng)格圖片...
});
//如果觸摸的是空白位置,直接返回不處理
if(gIndex==lastIndex) return;
let grid = grids[gIndex];
let lastGrid = grids[lastIndex];
//定義移動方向的偏移數(shù)據(jù)
let offsetMove;
if (grid.x==lastGrid.x){
//設(shè)置左右移動...
}else if(grid.y==lastGrid.y){
//設(shè)置上下移動...
}
//如果沒有可移動的,直接返回不處理
if (!offsetMove) return;
//...處理交換圖片后,更新索引
this.data.lastIndex = gIndex;
//開始移動圖片動畫
this.startMoveAnimation(lastIndex,offsetMove);
},
//...
})
游戲動畫
這里實(shí)現(xiàn)開始移動動畫的效果,
開始移動圖片的方法是startMoveAnimation(lastIndex,offsetMove)
,實(shí)現(xiàn)稍微復(fù)雜一點(diǎn),邏輯代碼如下
const AnimationUpdateDelay=10;//更新動畫延遲ms
const MovingPixelsOffset=5;//每次動畫移動的單位距離px
Page({
//...
/**
* 開始移動圖片動畫
*/
startMoveAnimation(lastIndex,offsetMove){
const { grids, gridSize:gs, node:canvas } = this.data.canvas;
//定義移動單位距離
const offset = MovingPixelsOffset;
const activeGrid = grids[lastIndex];
//此處省略了一些處理邏輯...
//定義動畫結(jié)束方法
const endAnimation = () => {
this.reDraw(true);
this.data.isAnimation = false;
this.isEndGame();
};
//定義動畫更新方法
const updateAnimation = () => {
//判斷條件,更新移動數(shù)據(jù)
if(offsetMove.x<0 && activeGrid.offsetX<0) activeGrid.offsetX+=offset;
//其余的一些判斷邏輯省略了...
else {
endAnimation();
return;
}
this.reDraw();
//繼續(xù)下一個(gè)更新動畫
// this.data.timer = setTimeout(()=>{
canvas.requestAnimationFrame(() => updateAnimation());
// },AnimationUpdateDelay);
};
//設(shè)置動畫進(jìn)行中
this.data.isAnimation = true;
//開始更新動畫
updateAnimation();
},
/**
* 判斷游戲是否結(jié)束(通關(guān))
*/
isEndGame(){
const { grids } = this.data.canvas;
let isEnd = grids.every(function (current,index) {
//...判斷邏輯,檢查網(wǎng)格集合里所有的圖片順序是否正確
});
//如果結(jié)束,就是順序正確,彈出提示用戶
if(isEnd){
this.data.isEnd=true;
wx.showModal({
title: '游戲結(jié)束',
content: `恭喜攻關(guān)!用時(shí)${this.data.timerNum}秒`,
confirmText:'重新開始',
complete: (res) => {
if (res.cancel) {
this.data.lastIndex=-1;
this.reDraw();
return;
}
if (res.confirm) {
this.reStart();
}
}
})
}
},
//...
})
游戲是否結(jié)束判斷方法
isEndGame()
,在移動動畫結(jié)束時(shí)會調(diào)用
寫到這里,拼圖游戲講解到此結(jié)束,理清了上面整個(gè)游戲思路嗎,相信自己能做到吧,
關(guān)于項(xiàng)目
項(xiàng)目源碼請?jiān)谶@里找點(diǎn)這里查看項(xiàng)目源碼,在資源一欄下,其中有個(gè)叫拼圖游戲的就是它,可以下載來看,謝謝支持!
運(yùn)行測試
打開項(xiàng)目源碼,游戲運(yùn)行效果動圖如下,
拼圖游戲里面圖片是可以更換的,換個(gè)自己喜歡的圖片,這樣才有新鮮感
如果有遇到什么問題請留言,作者會抽時(shí)間解答疑惑。文章來源:http://www.zghlxwxcb.cn/news/detail-494071.html
應(yīng)用場景
這個(gè)拼圖游戲應(yīng)用場景,用于解鎖攻關(guān)最合適不過文章來源地址http://www.zghlxwxcb.cn/news/detail-494071.html
- ?? 我的夢有一把鎖,我的心中一條河,等待有人開啟有人穿越~??
到了這里,關(guān)于【拼圖】拼圖游戲-微信小程序開發(fā)流程詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!