功能說明:
在一分鐘內,使用滑鼠按左鍵,在畫布上圈泡泡,其中泡泡的分數分別為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);
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; };
這樣的話軌跡就不是一個嚴格的多邊形,因為它多出了藍色和綠色的部分。 因此我們需要對圈出來的多邊形進行一個修正操作,使其變成一個真正的閉合多邊形:
/* 重置线段 */ 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 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;//包含 },
另外需要注意的是,由于射线与多边形相交交点个数是通过射线和多边形的每条边是否相交来判断,所以如果射线通过多边形的顶点,我们得出的结果就是相交了两次(通过顶点使射线与两条边都有相交)。因此我们需要记录判断过的交点,每次判断时检查该交点是否已经出现过,若出现过则不纳入计数,这样就基本实现了判断小球是否在鼠标圈选的多边形区域内。
以上是HTML5實現圈泡泡遊戲的程式碼分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!