用canvas寫個接水管小游戲


  聲明:本文為原創文章,如需轉載,請注明來源WAxes,謝謝!

  過年的十八天假期迷迷糊糊一下子就過去了(LZ還是實習生,鑒於大學最后一個寒假了,所以就多請了好多天假),又要返工上班了。這是年后的第一篇博客了。其實就是水一下,畢竟不能冷落博客太久啊。

  這兩天剛好抱着玩的心情寫了個接水管游戲,本來用css3更容易寫。。。就旋轉那些東西,不過因為LZ比較喜歡canvas的寫法,所以還是用了canvas來寫。

  先上個DEMO吧:接水管   玩法很簡單:點擊水管塊就可以旋轉水管,連通水管后點擊上面的水閥,然后就可以了。可能還有bug,LZ暫時沒去測試了。

  因為代碼也很簡單,重點也不多,就先說下游戲思路:

  【游戲邏輯】

  游戲邏輯很簡單,就是水管總共有4個面,給四個面一個代號:0,1,2,3;然后水管有兩個口,一個進水口,一個出水口,就可以用0,1,2,3來表示水管的兩個口(如果想寫更復雜的,多個口的,也一個樣,一個進水口,多個出水口,用數組保存就行了。我寫的這個比較簡單,只有兩個口,所以后面判斷連通性也比較容易。),保存好水管塊的進出口數據后,當水管旋轉時,相應改變水管進出口參數就行了。

  然后就是連通性判斷:水管只有兩個口的話就很簡單了。一般有一個最初的進水口,然后就從那個水管塊開始,先獲取出水口,然后再遍歷當前水管塊周圍的水管塊的進水口是否跟當前水管塊的出水口相連通(被遍歷的水管塊的進水口和出水口都要進行判斷,因為水管可以旋轉,也就是說進水口和出水口在一開始是無法確定的,當確定了連通關系后,就可以確定水管的進水口和出水口了,之后的循環也一樣),如果對的上。就說明連通了。然后就跳到那個水管塊,再進行那個水管塊周圍的水管塊的遍歷,就這樣重復遍歷,直到最后一個也就是出水的那個水管塊也連通了,就說明整條水管連通了。

  上面的是只有兩個口的水管連通性判斷,如果游戲中有三個口的水管甚至四個口的水管,判斷方式就沒那么簡單了。我寫的是兩個口的,不過也想了一下多口的判斷,大概就是找到水管的進水口,因為是多個出水口,所以就得一個一個來,對每一條線路進行判斷,每當跳轉到一個新的水管塊,就把之前的那個水管塊保存為新水管塊的父類水管。然后對新水管的出水口進行循環判斷,如果沒有發現有連通的水管,則跳轉至父類水管,並將新水管塊加入“此路不通”列表,再次遍歷父類水管塊的其他出水口進行判斷,如此循環,當循環到父類為最初進水口的那個水管塊的時候,就說明水管沒有連通路線,當然,如果循環到新水管塊為出口水管時,就說明連通了。

  樓主比較懶,所以就只寫了兩個口的,沒寫多個口的了。

  【代碼部分】

  首先定義水管塊對象:

var Box = function(center , style , Gateway , angle , coordinate){
                    this.center = center;
                    this.style = style;
                    this.angle = angle;
                    this.endangle = angle;
                    this.Gateway = Gateway;
                    this.coordinate = coordinate;
                }
                Box.prototype = {
                    constructor:Box,
                    draw:function(){
                        this.setHole();

                        if(this.angle!==this.endangle){
                            canclick = false;
                            this.rotate();
                        }
                        
                        var ext = [
                            {x:0,y:-boxWidth/2},
                            {x:boxWidth/2,y:0},
                            {x:0,y:boxWidth/2},
                            {x:-boxWidth/2,y:0},
                        ]
                        ctx.save();
                        ctx.translate(this.center.x , this.center.y);
                        ctx.rotate(this.angle);
                        //畫管道
                        switch(this.style){
                            case 0:break;
                            case 1:ctx.drawImage(document.getElementById("pipe1") , -boxWidth/2 , -boxWidth/2 , boxWidth , boxWidth);
                            break;
                            case 2:ctx.drawImage(document.getElementById("pipe2") , -boxWidth/2 , -boxWidth/2 , boxWidth , boxWidth);
                            break;
                            case 3:ctx.drawImage(document.getElementById("start") , -boxWidth/2 , -boxWidth/2 , boxWidth , boxWidth);
                            break;
                        }
                        ctx.restore();
                    },
                    rotate:function(){
                        if(Math.abs(this.endangle-this.angle)<=0.01){
                            canclick = true;
                            this.endangle = this.endangle>=2*Math.PI?0:this.endangle;
                            this.angle = this.endangle;

                            if(this.style===3){
                                var result = connectPipes();
                                if(result){
                                    // alert('成功連通')
                                    connectSuccess = true;
                                }
                                else {
                                    alert("游戲失敗")
                                    window.location.reload();
                                }
                            }
                        }
                        else {
                            this.angle += (this.endangle-this.angle)*0.2;
                        }
                    },
                    setHole:function(){
                        if(this.style===1){
                            var zl = this.endangle/(0.5*Math.PI);
                            var initHole1 = 0 , initHole2 = 2;
                            this.inHole = (initHole1+zl)>=4?((initHole1+zl)-4):(initHole1+zl);
                            this.outHole = (initHole2+zl)>=4?((initHole2+zl)-4):(initHole2+zl);
                        }
                        else if(this.style===2){
                            var zl = this.endangle/(0.5*Math.PI);
                            var initHole1 = 1 , initHole2 = 2;
                            this.inHole = (initHole1+zl)>=4?((initHole1+zl)-4):(initHole1+zl);
                            this.outHole = (initHole2+zl)>=4?((initHole2+zl)-4):(initHole2+zl);
                        }
                    }
                }

對象主要屬性包括:水管位置,水管種類(主要就兩個,1為直線的,2為九十度折角的),水管是否可以旋轉的判定,水管旋轉的初始角度,以及水管所處位置的行和列。

如何讓水管點擊后旋轉,其實也很簡單,就是先把畫布平移到水管塊中心,然后旋轉,然后再把水管畫出來,就旋轉好了。

 

然后就是判斷水管連通性的代碼:

//判斷水管連通性
                function connectPipes(){
                    var index = 0;
                    while(1){
                        var result = getHole(boxes[index]);
                        if(boxes[index+result.nextBox]){
                            if(result.hole===boxes[index+result.nextBox].inHole){
                                index = index+result.nextBox;
                            }
                            else if(result.hole===boxes[index+result.nextBox].outHole){
                                var num = boxes[index+result.nextBox].inHole;
                                boxes[index+result.nextBox].inHole = result.hole;
                                boxes[index+result.nextBox].outHole = num;
                                index = index+result.nextBox;
                            }
                            else {
                                break;
                            }
                        }
                        else {
                            break;
                        }
                    }
                    if(index===boxes.length-1) return true;
                    else return false;
                }

                function getHole(box){
                    var hole="0";
                    var nextBox=0;
                    switch(box.outHole){
                        case 0 : hole = 2;
                                nextBox = -cols;
                        break;
                        case 1 : hole = 3;
                                if(box.coordinate.cols===cols-1){
                                    nextBox = 1000000;
                                }
                                else nextBox = 1;
                        break;
                        case 2 : hole = 0;
                                nextBox = cols;
                        break;
                        case 3 : hole = 1;
                                if(box.coordinate.cols===0){
                                    nextBox = 1000000;
                                }
                                else nextBox = -1;
                        break;
                    }
                    return {hole:hole , nextBox:nextBox};
                }

邏輯之前已經說過了,而且代碼也比較簡單,就不解釋了,getHole是返回當前水管塊的出水口如果要連通,需要的進水口的參數以及水管在水管數組里的位置。

   然后就是路徑,如何保證一定有條成功的路呢?因為如果管道全部隨機的話,可能會陷入死路。所以,我就干脆定好幾條正確的路徑,每次刷新頁面就取其中一條,其他水管塊也添加一些隨機出來的水管作干擾。所以,我就專門用一個data.js文件來存放所有的路徑,路徑文件代碼如下:

//水管路徑
var allPath = [
[
    {rows:0,cols:0,style:1},
    {rows:1,cols:0,style:1},
    {rows:2,cols:0,style:1},
    {rows:3,cols:0,style:2},
    {rows:3,cols:1,style:2},
    {rows:2,cols:1,style:2},
    {rows:2,cols:2,style:1},
    {rows:2,cols:3,style:1},
    {rows:2,cols:4,style:1},
    {rows:2,cols:5,style:1},
    {rows:2,cols:6,style:1},
    {rows:2,cols:7,style:2},
    {rows:3,cols:7,style:1},
],

[
    {rows:0,cols:0,style:1},
    {rows:1,cols:0,style:1},
    {rows:2,cols:0,style:2},
    {rows:2,cols:1,style:2},
    {rows:1,cols:1,style:2},
    {rows:1,cols:2,style:2},
    {rows:2,cols:2,style:2},
    {rows:2,cols:3,style:1},
    {rows:2,cols:4,style:1},
    {rows:2,cols:5,style:1},
    {rows:2,cols:6,style:2},
    {rows:1,cols:6,style:2},
    {rows:1,cols:7,style:2},
    {rows:2,cols:7,style:1},
    {rows:3,cols:7,style:1},
],

[
    {rows:0,cols:0,style:1},
    {rows:1,cols:0,style:1},
    {rows:2,cols:0,style:1},
    {rows:3,cols:0,style:2},
    {rows:3,cols:1,style:2},
    {rows:2,cols:1,style:1},
    {rows:1,cols:1,style:2},
    {rows:1,cols:2,style:1},
    {rows:1,cols:3,style:1},
    {rows:1,cols:4,style:1},
    {rows:1,cols:5,style:2},
    {rows:1,cols:5,style:2},
    {rows:2,cols:5,style:2},
    {rows:2,cols:6,style:1},
    {rows:2,cols:7,style:2},
    {rows:3,cols:7,style:1},
],

]
View Code

路徑的每一個點包含的參數就是行列位置以及水管的類型,如果想游戲更多變化,可以再多加幾條路徑,我就只弄了三條啦:下面是取路徑然后生成相對應的水管,同時把水管的旋轉角度也隨機。

var n = getRandom(0 , allPath.length-1);
                    var path = allPath[n];
                    path.foreach(function(){
                        var index = this.rows*cols + this.cols;
                        if((this.rows==0&&this.cols==0)||(this.rows==(rows-1)&&this.cols==(cols-1))){
                            boxes[index] = new Box({x:(this.cols*boxWidth)+boxWidth/2+jiange , y:(this.rows*boxWidth)+boxWidth/2+marginTop} , this.style , true , 0 , {rows:this.rows , cols:this.cols});
                        }
                        else if(this.rows>0&&this.rows<3&&this.cols>2&&this.cols<5){
                            boxes[index] = new Box({x:(this.cols*boxWidth)+boxWidth/2+jiange , y:(this.rows*boxWidth)+boxWidth/2+marginTop} , this.style , true , 0.5*Math.PI ,  {rows:this.rows , cols:this.cols});
                        }
                        else{
                            boxes[index] = new Box({x:(this.cols*boxWidth)+boxWidth/2+jiange , y:(this.rows*boxWidth)+boxWidth/2+marginTop} , this.style , false , parseInt(getRandom(0,3))*0.5*Math.PI ,  {rows:this.rows , cols:this.cols});
                        }
                    });

 

然后就木有啦。。。。

源碼地址:https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Game-demo/connectPipe


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM