【CSON原創(chuàng)】HTML5圈泡泡游戲發(fā)布
功能說(shuō)明:
在一分鐘內(nèi),使用鼠標(biāo)按著左鍵,在畫(huà)布上圈泡泡,其中泡泡的分值分別為10(白)、20(淺藍(lán))、30(黃)、-10(紅)、-20(綠)、-30(深藍(lán))分,可以一次圈多個(gè)泡泡,倒計(jì)時(shí)結(jié)束即計(jì)算總分值,該游戲基于cnGameJS。
效果預(yù)覽:
實(shí)現(xiàn)分析:
首先每個(gè)小球定義一個(gè)ball類,由于小球需要使用圖片,并且有一定的尺寸和運(yùn)動(dòng),所以使該類繼承cnGameJS的sprite類。ball類除了擁有x,y坐標(biāo)外,還擁有一個(gè)z坐標(biāo),該坐標(biāo)用于使小球具有離玩家遠(yuǎn)近的視覺(jué)差。
/* 小球?qū)ο? */ 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);
之后我們?yōu)樾∏蛱砑觬esetXY方法,該方法根據(jù)小球的z坐標(biāo),改變小球的位置以及尺寸,使小球有遠(yuǎn)近的視覺(jué)差。首先根據(jù)z計(jì)算縮放比scale,然后根據(jù)scale調(diào)整x,y和width,height,另外我們使小球z大于1000時(shí),小球消失,這樣就避免了小球過(guò)大而占據(jù)整個(gè)屏幕。
cg.core.extendProto(Ball,{ disappear:function(){//小球被選中消失 list.remove(this); }, resetXY:function(){//根據(jù)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]);//相對(duì)于現(xiàn)時(shí)的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(); } });
之后,為了管理多個(gè)小球,可以增加一個(gè)小球管理器。管理器負(fù)責(zé)動(dòng)態(tài)改變小球到玩家的距離以及使小球在畫(huà)布隨機(jī)的位置出現(xiàn):
/* 小球?qū)ο蠊芾砥? */ var ballsManager={ createDuration:200, ballSize:30, lastCreateTime:Date.now(), /* 隨機(jī)生成小球 */ 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)];//隨機(jī)獲得的小球種類和分值 var newBall=new Ball({x:x,y:y,size:this.ballSize,z:-280,score:randomKind[1]}); newBall.setCurrentImage(srcObj[randomKind[0]]);//設(shè)置圖片 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; } } }
關(guān)于小球管理就介紹到這里,之后主要介紹怎樣實(shí)現(xiàn)鼠標(biāo)的圈選。
如果我們?cè)诿看螏聲r(shí),根據(jù)鼠標(biāo)現(xiàn)時(shí)的位置以及上一次的位置繪制一條線段,那么鼠標(biāo)的移動(dòng)軌跡就可以被一條曲線表示出來(lái),該曲線由每次繪制的線段組成,因此我們也可以說(shuō)該曲線是一條由多條線段首尾相連組成的曲線。因此我們可以首先實(shí)現(xiàn)一個(gè)線段類:
/* 直線 */ 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); },
之后需要考慮的就是怎樣實(shí)現(xiàn)圈選了。當(dāng)我們用鼠標(biāo)畫(huà)出一個(gè)圈時(shí),每條小線段就組成了一個(gè)閉合的多邊形,這時(shí)我們就可以說(shuō)鼠標(biāo)圈出了一個(gè)閉合區(qū)域,之后就可以進(jìn)一步計(jì)算哪些小球在該區(qū)域里面。
但是怎樣判斷鼠標(biāo)是否圈出了一個(gè)閉合區(qū)域呢?這里使用的方法是:遍歷每一條線段,從該線段的下一條再下一條線段開(kāi)始遍歷余下的線段,判斷它們中是否有線段和開(kāi)始的那條線段相交,如果相交,則證明曲線閉合了。注意這里從線段的下條再下條線段開(kāi)始遍歷是為了跳過(guò)線段首尾相連的情況。(例如,第一條線段肯定和第二條線段相交,因此從第三條線段開(kāi)始判斷,跳過(guò)相鄰線段收尾相交的情況),代碼如下:
/* 返回軌跡是否閉合 */ 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);//交點(diǎn)坐標(biāo) if(point){//非連接的相交 resetLineSegs(lines,i,j,point); hasClosed=true; return true; } } } } return false; };
isCross方法返回線段交點(diǎn)的坐標(biāo),我們獲得該坐標(biāo)后,還需要把多邊形修正成為真正的多邊形,因?yàn)橛檬髽?biāo)圈出來(lái)的多邊形并不是一個(gè)真正的多邊形,它的開(kāi)始和結(jié)束部分很可能會(huì)有突出的地方,如下圖:

我們假設(shè)鼠標(biāo)從綠色部分開(kāi)始圈一個(gè)圈,到藍(lán)色部分結(jié)束。這樣的話軌跡就并不是一個(gè)嚴(yán)格的多邊形,因?yàn)樗喑隽怂{(lán)色和綠色的部分。因此我們需要對(duì)圈出來(lái)的多邊形進(jìn)行一個(gè)修正操作,使其變成一個(gè)真正的閉合多邊形:
/* 重置線段 */ 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]); } }
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)"});
判斷小球是否在多邊形里,可以轉(zhuǎn)化為判斷小球的中點(diǎn)是否在多邊形里,這里使用的方法叫射線法,意思是從一點(diǎn)向左發(fā)射出一條射線,如果射線和多邊形有奇數(shù)個(gè)交點(diǎn),則證明點(diǎn)在多邊形內(nèi)部。根據(jù)該定理實(shí)現(xiàn)的isInside方法如下:
/** *判斷某點(diǎn)是否在多邊形內(nèi)(射線法) **/ isInside:function(point){ var lines=this.getLineSegs(); var count=0;//相交的邊的數(shù)量 var lLine=new Line({start:[point[0],point[1]],end:[-9999,point[1]]});//左射線 var crossPointArr=[];//相交的點(diǎn)的數(shù)組 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++){ //如果交點(diǎn)和之前的交點(diǎn)相同,即表明交點(diǎn)為多邊形的頂點(diǎn) 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;//包含 },
大概就說(shuō)這么多了,歡迎交流~
demo下載地址:點(diǎn)擊下載
歡迎轉(zhuǎn)載,請(qǐng)標(biāo)明出處:http://www.rzrgm.cn/Cson/archive/2012/04/09/2439127.html
功能說(shuō)明:
在一分鐘內(nèi),使用鼠標(biāo)圈泡泡,其中泡泡的分值分別為10、20、30、-10、-20、-30分,可以一次圈多個(gè)泡泡,倒計(jì)時(shí)結(jié)束即計(jì)算總分值,該游戲基于cnGameJS。
浙公網(wǎng)安備 33010602011771號(hào)