【CSON原創】HTML5圈泡泡游戲發布


功能說明:

  在一分鍾內,使用鼠標按着左鍵,在畫布上圈泡泡,其中泡泡的分值分別為10(白)、20(淺藍)、30(黃)、-10(紅)、-20(綠)、-30(深藍)分,可以一次圈多個泡泡,倒計時結束即計算總分值,該游戲基於cnGameJS

效果預覽:

 
實現分析: 
 首先每個小球定義一個ball類,由於小球需要使用圖片,並且有一定的尺寸和運動,所以使該類繼承cnGameJS的sprite類。 ball類除了擁有x,y坐標外,還擁有一個z坐標,該坐標用於使小球具有離玩家遠近的視覺差。
/*    小球對象    */
var Ball=function(opt){
    this.parent.call(this,opt);
    this.oriPos=[this.x+this.width/2,this.y+this.height/2];
    this.oriSize=opt.size;
    this.z=opt.z||0;
    this.score=opt.score||0;
    this.oriSpeedZ=4+Math.random()*4;
    this.scale=1;
    this.resetXY();
    
}
cg.core.inherit(Ball,Sprite);
  之后我們為小球添加resetXY方法,該方法根據小球的z坐標,改變小球的位置以及尺寸,使小球有遠近的視覺差。首先根據z計算縮放比scale,然后根據scale調整x,y和width,height,另外我們使小球z大於1000時,小球消失,這樣就避免了小球過大而占據整個屏幕。
cg.core.extendProto(Ball,{
    disappear:function(){//小球被選中消失
        list.remove(this);
    },
    resetXY:function(){//根據Z改變x,y的位置和尺寸
        var oriX=this.oriPos[0];
        var oriY=this.oriPos[1];
        var oriSize=this.oriSize;
        this.scale=((center[0]+this.z)/center[0]);//相對於現時的scale        
        this.x=(oriX-center[0])*this.scale+center[0];
        this.y=(oriY-center[1])*this.scale+center[1];
        this.height=this.width=this.oriSize*this.scale;
        this.speedZ=this.oriSpeedZ*this.scale;
        if(this.z>1000){
            this.disappear();
        }
    },
    update:function(){
        this.parent.prototype.update.call(this);
        this.resetXY();
    }
});

  

  之后,為了管理多個小球,可以增加一個小球管理器。管理器負責動態改變小球到玩家的距離以及使小球在畫布隨機的位置出現:
  
/*    小球對象管理器    */
var ballsManager={
    createDuration:200,
    ballSize:30,
    lastCreateTime:Date.now(),
    /*    隨機生成小球    */
    createRandomBalls:function(num){
        var now=Date.now();
        if(now-this.lastCreateTime>this.createDuration){
            for(var i=0;i<num;i++){
                var x=Math.random()* cg.width;
                var y=Math.random()* cg.height;
                var randomKind=ballKinds[Math.floor(Math.random()*6)];//隨機獲得的小球種類和分值    
                var newBall=new Ball({x:x,y:y,size:this.ballSize,z:-280,score:randomKind[1]});
                newBall.setCurrentImage(srcObj[randomKind[0]]);//設置圖片
                list.add(newBall);
            }
            this.lastCreateTime=now;
        }
    },
    /*    改變小球位置    */
    changeBallsPos:function(){
        var ballsArr=list.get(function(elem){
            return elem instanceof Ball;                               
        });
        for(var i=0,len=ballsArr.length;i<len;i++){
            var ball=ballsArr[i];
            ball.z+=ball.speedZ;    
        }
    }
}
  
  關於小球管理就介紹到這里,之后主要介紹怎樣實現鼠標的圈選。
  如果我們在每次幀更新時,根據鼠標現時的位置以及上一次的位置繪制一條線段,那么鼠標的移動軌跡就可以被一條曲線表示出來,該曲線由每次繪制的線段組成,因此我們也可以說該 曲線是一條由多條線段首尾相連組成的曲線。因此我們可以首先實現一個線段類:
  
        /*    直線    */
        var line=function(options){
            if (!(this instanceof arguments.callee)) {
                return new arguments.callee(options);
            }
            this.init(options);
        }
    
        
        line.prototype = {
            /**
            *初始化
            **/
            init: function(options) {    
                this.start=[0,0];
                this.end=[0,0];    
                this.style="red";
                this.lineWidth=1;
                this.context=cg.context;
                options = options || {};
                cg.core.extend(this,options);
            },
  該類保存線段的起始點坐標和結束點坐標,以及寬度,樣式等。
 
  之后需要考慮的就是怎樣實現圈選了。當我們用鼠標畫出一個圈時,每條小線段就組成了一個閉合的多邊形,這時我們就可以說鼠標圈出了一個閉合區域,之后就可以進一步計算哪些小球在該區域里面。
 
  但是怎樣判斷鼠標是否圈出了一個閉合區域呢?這里使用的方法是: 遍歷每一條線段,從該線段的下一條再下一條線段開始遍歷余下的線段,判斷它們中是否有線段和開始的那條線段相交,如果相交,則證明曲線閉合了。注意這里從線段的下條再下條線段開始遍歷是為了跳過線段首尾相連的情況。(例如,第一條線段肯定和第二條線段相交,因此從第三條線段開始判斷,跳過相鄰線段收尾相交的情況),代碼如下:
  
    /*    返回軌跡是否閉合    */
    var isClose=function(lines){    
        var hasClose=false;
        for(var i=0;i<lines.length;i++){
            var l1=lines[i];
            for(var j=i+2;j<lines.length;j++){
                var l2=lines[j];
                if(l2){
                    var point=l1.isCross(l2);//交點坐標
                    if(point){//非連接的相交
                        resetLineSegs(lines,i,j,point);
                        hasClosed=true;
                        return true;
                    }
                }
            }
        }
        
        return false;
    };                  
   
  isCross方法返回線段交點的坐標,我們獲得該坐標后,還需要把多邊形修正成為真正的多邊形,因為用鼠標圈出來的多邊形並不是一個真正的多邊形,它的開始和結束部分很可能會有突出的地方,如下圖:
  我們假設鼠標從綠色部分開始圈一個圈,到藍色部分結束。 這樣的話軌跡就並不是一個嚴格的多邊形,因為它多出了藍色和綠色的部分。因此我們需要對圈出來的多邊形進行一個修正操作,使其變成一個真正的閉合多邊形:
  
    /*    重置線段    */
    var resetLineSegs=function(lines,i,j,point){
        lines[i].end[0]=point[0];
        lines[i].end[1]=point[1];
        lines[i+1].start[0]=point[0];
        lines[i+1].start[1]=point[1];
        
        lines[j].start[0]=point[0];
        lines[j].start[1]=point[1];
    
        lines[j-1].end[0]=point[0];
        lines[j-1].end[1]=point[1];
        for(var m=i+1;m<j;m++){
            closedLineSegsArr.push(lines[m]);
        }    
    }
  當我們判斷到兩條線段相交后,可以獲取到這兩條線段的索引,這里分別為i和j(i<j),point為交點。在新增加的resetLineSegs方法中,首先改變i線段的結束坐標,使其剛好為交點,再改變j線段的起始坐標,使其為交點,並且使i線段后面的線段的起始坐標以及j線段前面線段的結束坐標為交點,這樣就可以使多邊形真正閉合。之后把閉合多邊形各個邊的對象保存在一個數組中,我們后面就要使用這些邊,構造多邊形對象了。

                        for(var i=0,len=closedLineSegsArr.length;i<len;i++){
                            pointsArr.push([closedLineSegsArr[i].start[0],closedLineSegsArr[i].start[1]]);    
                        }
                        polygon=new Polygon({pointsArr:pointsArr,style:"rgba(241,46,8,0.5)"});
  通過多邊形的邊對象的數組,可以獲取到多邊形每個頂點的坐標,並根據這些坐標構造多邊形對象,之后的工作就是判斷小球是否在多邊形里面了。
  判斷小球是否在多邊形里,可以轉化為判斷小球的中點是否在多邊形里,這里使用的方法叫 射線法,意思是 從一點向左發射出一條射線,如果射線和多邊形有奇數個交點,則證明點在多邊形內部。根據該定理實現的isInside方法如下:
            /**
            *判斷某點是否在多邊形內(射線法)
            **/
            isInside:function(point){
                var lines=this.getLineSegs();

                var count=0;//相交的邊的數量
                var lLine=new Line({start:[point[0],point[1]],end:[-9999,point[1]]});//左射線
                var crossPointArr=[];//相交的點的數組
                for(var i=0,len=lines.length;i<len;i++){
                    var crossPoint=lLine.isCross(lines[i]);
                    if(crossPoint){
                        for(var j=0,len2=crossPointArr.length;j<len2;j++){
                            //如果交點和之前的交點相同,即表明交點為多邊形的頂點
                            if(crossPointArr[j][0]==crossPoint[0]&&crossPointArr[j][1]==crossPoint[1]){
                                break;    
                            }
                            
                        }
                        if(j==len2){
                            crossPointArr.push(crossPoint);    
                            count++;
                        }
                        
                    }
                }
        
                if(count%2==0){//不包含
                    return false;
                }
                return true;//包含
            },
  另外需要注意的是,由於射線與多邊形相交交點個數是通過射線和多邊形的每條邊是否相交來判斷,所以如果射線通過多邊形的頂點,我們得出的結果就是相交了兩次(通過頂點使射線與兩條邊都有相交)。 因此我們需要記錄判斷過的交點,每次判斷時檢查該交點是否已經出現過,若出現過則不納入計數,這樣就基本實現了判斷小球是否在鼠標圈選的多邊形區域內。
  大概就說這么多了,歡迎交流~
 
   demo下載地址:點擊下載
  歡迎轉載,請標明出處: http://www.cnblogs.com/Cson/archive/2012/04/09/2439127.html
  
  


免責聲明!

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



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