微信小程序——用canvas做個層疊消融游戲(一)

本篇為小程序的一學(xué)習(xí)筆記,以一小游戲‘層疊消融’為例子,對canvas組件進行展開學(xué)習(xí)。

開場白就沒了,直奔主題。先來看看小程序在組件使用上,對canvas做了什么。

em……,首先,指定了作為唯一標(biāo)識符的屬性canvas-id,因為小程序搭了一套MV*框架,所以對于元素綁定這個,和用原生的js的方式不同。然后就是一系列的手勢觸發(fā)事件。其他的,其實在使用上也沒做什么特別的處理嘛。既然如此,那就直接貼上對canvas進行元素綁定的代碼吧。

<!-- canvas.wxml -->

<canvas canvas-id="testCanvas"></canvas>

<!-- canvas.js -->

Page({

????onReady: function(e) {

????????// 使用wx.createContext獲取繪圖上下文 context

????????var context = wx.createCanvasContext('testCanvas');

????????context.setStrokeStyle('#00ff00');

????????context.rect(0,0,200,200);

????????context.draw();

????}

})


因為小程序的canvas元素默認寬高是不會覆蓋全屏的,所以在初始化canvas之后要進行重新設(shè)置寬高。屏幕的寬高可以通過API:wx.getSystemInfo獲得。


接下來就是畫圖了?!畬盈B消融’這個游戲的主要效果就是圖片疊加部分雙數(shù)相消。具體效果如圖:


上面的圖形由三個基本圖形(正方形)組成,用這三個基本圖形拼出上面的目標(biāo)圖案,則算通關(guān)。

需求:多圖形相疊雙數(shù)相消。要實現(xiàn)這個效果,用傳統(tǒng)的css是很麻煩的,這時canvas的靈活性就體現(xiàn)出來了。canvas對圖片圖形的處理是像素級的,其本身提供了一系列的圖形遮蓋策略。globalCompositeOperation這個屬性設(shè)定了在畫新圖形時采用的遮蓋策略,其值是一個標(biāo)識12種遮蓋方式的字符串。其中‘xor’方式正能實現(xiàn)我們雙數(shù)相消的需求。

立即貼上主要的代碼:

//繪制一個反相圖案

let revert = function(path){

????this.context.beginPath();

????this.context.fillStyle="#000";

????this.context.globalCompositeOperation="xor";

????for(let i=0;i<path.length;i++){

????????if(i==0){

????????????this.context.moveTo(path[i].x,path[i].y);

????????}else{

????????????this.context.lineTo(path[i].x,path[i].y);

????????}

????}

????this.context.fill();

????return this;

}


(小程序的onReady函數(shù),這里具體的調(diào)用我打包了一下,具體打包細節(jié)我就不再這里細講了)

onReady: function(){

????let canvasCtl = new this.canvasCtl();

????canvasCtl.init(this);

????let block1 = new util.block([{x:200,y:50},{x:100,y:150},{x:100,y:50}],canvasCtl);

????block1.initBlock();

????let block2 = new util.block([{x:150,y:70},{x:250,y:70},{x:250,y:200}],canvasCtl);

????block2.initBlock();

????let block3 = new util.block([{x:50,y:60},{x:110,y:60},{x:110,y:110},{x:50,y:110}],canvasCtl);

????block3.initBlock();

????this.blockList.push(block1);

????this.blockList.push(block2);

????this.blockList.push(block3);

},

效果圖如下:


既然是小游戲,那就要會動是不是。需求:手指點上時,選中圖片跟著移動。

這時,就要用上小程序給我們提供的手勢觸發(fā)了,用到的有這兩個:


bindtouchstart用于判斷手指選中那個圖形,bindtouchmove用于圖形移動。

對于判定選中圖形這個問題,趕緊回憶起你的小學(xué)數(shù)學(xué):沿著判定點向圖形方向畫一條射線,若相交點為單數(shù),則判定點在圖形中,否則,判定點在圖形外。轉(zhuǎn)換為代碼模式如下:

block.prototype.checkPointInRegion = function(pt){

????let nCross = 0; // 定義變量,統(tǒng)計目標(biāo)點向右畫射線與多邊形相交次數(shù)

????for (let i = 0; i < this.path.length; i++) { //遍歷多邊形每一個節(jié)點

????????let p1 = this.path[i];

????????let p2 = this.path[(i+1)%this.path.length];

????????// p1是這個節(jié)點,p2是下一個節(jié)點,兩點連線是多邊形的一條邊

????????// 以下算法是用是先以y軸坐標(biāo)來判斷的

????????if ( p1.y == p2.y )continue;//如果這條邊是水平的,跳過

????????if ( pt.y < ((p1.y= ((p1.y>p2.y)?p1.y:p2.y))continue;//如果目標(biāo)點高于這個線段,跳過

????????//那么下面的情況就是:如果過p1畫水平線,過p2畫水平線,目標(biāo)點在這兩條線中間

????????let x = (pt.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;

????????// 這段的幾何意義是 過目標(biāo)點,畫一條水平線,x是這條線與多邊形當(dāng)前邊的交點x坐標(biāo)

????????if ( x > pt.x ) nCross++; //如果交點在右邊,統(tǒng)計加一。這等于從目標(biāo)點向右發(fā)一條射線(ray),與多邊形各邊的相交(crossing)次數(shù)

????}

????if (nCross % 2 == 1) {

????????return true; //如果是奇數(shù),說明在多邊形里

????} else {

????????return false; //否則在多邊形外 或 邊上

????}

}

下面就是移動了,canvas的圖形的移動很粗暴,那就是擦掉舊的重新畫上新的。需要記錄下多次bindtouchmove觸發(fā)時手指的位移->計算出圖形移動后的新定點坐標(biāo)->在畫布上擦掉舊圖形->根據(jù)新坐標(biāo)畫上新圖形。下面貼上主要代碼:

searchBlock:function(e){?

????if(this.movePath.length>10){?

?????????this.movePath.shift();?

?????}

?????this.movePath.push(e);

? ??for(let i = 0; i < this.blockList.length; i++){//讓新選中的圖形永遠處于第一位

? ? ? if(this.blockList[i].checkPointInRegion({ x: e.touches[0].x , y:e.touches[0].y })){

? ? ? ? [this.blockList[0],this.blockList[i]] = [this.blockList[i],this.blockList[0]]

? ? ? };

? ? }

? },?

?moveBlock:function(e){

? ? if(this.movePath.length>10){

? ? ? this.movePath.shift();

? ? }

? ? this.movePath.push(e);

? ? let oldPath = this.movePath[this.movePath.length-2]?this.movePath[this.movePath.length-2]:null;

? ? let newPath = e;

? ? this.blockList[0].move(oldPath.touches[0],newPath.touches[0]);

? },

block.prototype.move = function(oldPath,newPath){

? ? if(oldPath){

? ? ? var x = newPath.x - oldPath.x;

? ? ? var y = newPath.y - oldPath.y;

? ? ? this.changeLocation(x,y);

? ? }

}


效果圖如下:


接下去就是通關(guān)判定、關(guān)卡設(shè)置、關(guān)卡選擇……有點多,這些內(nèi)容就留到下期吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容