機能の説明:
1 分以内に、 マウスの左ボタンを使用して、キャンバス上のバブル を円で囲みます。バブル スコアは 10 (白)、20 (水色)、30 (黄色) です。 )、-10 (赤)、-20 (緑)、-30 (紺) ポイント。一度に複数のバブルを丸で囲むことができます。このゲームは、cnGameJS
に基づいています。 。効果のプレビュー:
実装分析:
まず、各ボールは画像を使用する必要があり、特定のサイズと動きを持つため、このクラスは cnGameJS のスプライトを継承します。クラス。 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 に基づいて拡大縮小率スケールを計算し、次にスケールに基づいて x、y、幅、高さを調整します。さらに、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); },
このクラスは、線分の始点座標と終点座標、および幅、スタイルなどを保存します。
次に考えなければならないのは、サークル選択をどのように実装するかです。マウスで円を描くと、それぞれの小さな線分が閉じた多角形を形成します。このとき、マウスが閉じた領域を囲んだと言え、その領域内にどの小さなボールがあるかをさらに計算できます。
しかし、マウスが閉じた領域を一周したかどうかをどうやって判断するのでしょうか?ここで使用される方法は次のとおりです。 各線分をトラバースし、次の線分とその次の線分から開始して残りの線分をトラバースし、それらのいずれかが開始線分と交差するかどうかを判断します。それらが交差していれば、それが証明されます。カーブは閉じています。 ここで次の線分と次の線分からトラバースする目的は、線分が端から端までつながっている状況をスキップすることであることに注意してください。 (たとえば、最初の線分は 2 番目の線分と交差する必要があるため、最後に隣接する線分の交点をスキップして、3 番目の線分から判断を開始します)、コードは次のとおりです。
/* 返回轨迹是否闭合 */ 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]); } }
2つの線分が交差すると判断すると、2つの線分のインデックスを取得できます。それぞれ 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 中国語 Web サイトの他の関連記事を参照してください。