飛機躲避小游戲-是男人就撐100秒的制作_Flash教程

      編輯Tag賺U幣
      教程Tag:暫無Tag,歡迎添加,賺取U幣!

      推薦:Flash播放器參數(shù)知多少?
      FlashOBJECT和EMBED標簽我們現(xiàn)在大部分人做網(wǎng)頁,都是直接用DW插入flash,而且DW也是所見即所得,直接生成了相應的flash顯示代碼。可是我們又有多少人了解這些

      摘要:
      可以將這個游戲的整體運作看成一個粒子系統(tǒng),再加上子彈和飛機的碰撞判定即可.簡單起見,這里的飛機采用球體.
      要害詞: 粒子系統(tǒng),飛機躲避游戲
      Small STG Game---Lasting 100 Secs's making process
      EmilMatthew(EmilMatthew@126.com)
      Abstract:
      We could treat this game as a particle system, which only needs the collide detect additionally.
      To be easy enough as a sample program, I use one ball to take place of the plane in the game.

      Key Words: Particle System, Plane avoids bullets game

      1前言:
      是男人就撐100秒是一個流行廣泛,但又略顯BT意味的小游戲。游戲的玩法就是四面不斷的有子彈射出,而你的任務就是控制你的飛機不斷的躲避,直到被擊中,以躲避時間的長短來評定游戲水平的高低。
      這個游戲在實現(xiàn)是比較輕易的,由于子彈在這里占據(jù)了主要地位,所以考慮以子彈為中心,即考慮構建一個粒子系統(tǒng),來控制子彈的發(fā)射,發(fā)射方向的計算,以及出界的判定等。至于飛機方面,則只要有控制的部分(事件驅動,事件監(jiān)聽或用循環(huán),要視具體實現(xiàn)環(huán)境而定),把兩者結合起來,只要加上飛機與子彈間的碰撞檢測即可,這里出于演示的目的,簡單起見,采用球代替飛機的造型。

      2子彈粒子系統(tǒng)的運作流程:
      子彈的粒子系統(tǒng)要控制好子彈的發(fā)射,發(fā)射方向的計算,出界的判定以及碰撞檢測.
      該粒子系統(tǒng)的總體框架并不困難,這里給出我實現(xiàn)過程中的總體框架:
      while(runFlag)
      {
      For all particles
      {
      If(current particle is not lived)
      {
      Init this particle.
      }
      Else if(current particle is out of the game area)
      {
      Current particle set to dead.
      }
      Else
      {
      Current particle move and show
      If(current particle is collided with the plane)
      runFlag=flase;
      }
      }
      } 這里要注重一下if….else if 中的條件判定對應的現(xiàn)實意義,即是否會出現(xiàn)實中無意義但在程序中卻出現(xiàn)的情況,假如出現(xiàn)的話,這樣的BUG將比較難抓出.
      比如這里,假如將if及else if 中語中的條件及對應的內容作相應的交換,即:
      if(current particle is out of the game area)
      {
      Current particle set to dead.
      }
      Else if(current particle is not lived)
      {
      Init this particle.
      } 在第一個if判定中,會將這樣一種情況被包括進去:
      if(current particle is out of game area&& current is not lived)此時,將導致第二個判定永遠無法到達.
      所以,當條件復雜且多的時候,最好是列張真值表,看看所有可能的情況是否都如期的到達該到的判定條件處,避免在程序調試中浪費過多不避要的時間.

      3子彈粒子設計細節(jié):
      3.1子彈粒子的數(shù)據(jù)結構及存儲方式.
      粒子類以一個類的形式進行封裝,里面包含了一些基本的物理屬性及粒子相關的一些動作(函數(shù))。簡要的情況如下:
      class SPhy.CSPhyMc extends MovieClip
      {
      public var v :Number=0;//1 demision Velocity or together Velocity of vx ,vy
      public var vx:Number=0;
      public var vy:Number=0;
      ……

      public function setLife(lifeValue:Number):Void
      {
      life=lifeValue;
      }

      public function getLife():Number
      {
      return life;
      }

      public function isLived():Boolean
      {
      return life==LIVED;
      }
      … …
      } 而在游戲中,這采用一個數(shù)組來實現(xiàn)粒子的群落,理由是使用方便而且快速。(當然,出于一種美學上的要求,你可能會選鏈表,因為它的插入和刪除來的比較漂亮和干凈,這就取決于你自己的喜好了)
      至于子彈起始的坐標值,則是隨機的散落于游戲屏幕區(qū)域外圍,要寫個相應的算法并不困難(詳見代碼部分)


      3.2 發(fā)射角的計算.
      發(fā)射角的計算相當于一道簡單的高中向量的題目:
      已知兩點P1(x1,y1),P2(x2,y2),求P1指向P2的單位向量a.
      求解:
      a) 計算兩點間的距離L=sqrt((x1-x2)^2 (y1-y2)^2)
      b) 求出P1P2(向量),P1P2=((x2-x1)/L,(y2-y1)/L);
      c) 單位化a=P1P2/(Len(P1P2))
      而你要做的,只是將子彈看成是P1,你自己的飛機看成是P2,即可,最后還應把起始速度去
      乘以所求得的單位向量a=(cos(fi),sin(fi))
      Vx=v*cos(fi)
      Vy =v*sin(fi)

      3.3 碰撞檢測
      碰撞檢測是個廣泛而重要的話題,可以從簡單到復雜,難度突破主要在計算幾何上。這里針對本游戲談兩個:
      3.3.1兩個圓的碰撞檢測,這個不用多說了,只要看兩個圓的圓心的距離是否比它們的半徑之和來的小就是了.
      即圓1有:圓心O1(x1,y1),半徑r1
      圓2有:圓心O2(x2,y2),半徑r2
      則它們之間的碰撞檢測可以這樣來做:
      If(Len(O1O2)<=r1 r2)
      {
      Two circles collide.
      }
      Else
      {
      Safe condition.
      } 假如在視覺效果要求比較高的場和,尤其是不答應出現(xiàn)物體重疊的場和,不仿在Len(O1O2) 后加上一個偏移值。這樣可以保證視覺上不會看到兩個物體重疊的現(xiàn)像,盡管在精確的數(shù)值模型上二者并未相碰。而在數(shù)值精度要求高的場和,恐怕情況就要反一下了,圖形是第二位的,數(shù)據(jù)的精準才是最重要的。具體如何去平衡圖形和數(shù)據(jù)間的對應關系,還請諸位自己去斟酌了。

      3.3.1圓和三角形間的碰撞檢測:
      三角形可以用通常用一個五元組Q(P1,P2,k0,k1,k2)來表達(許多飛機的外形通常可以看成一個三角形)
      對于Q(P1,P2,k0,k1,k2),其中,P1,P2是三角的位于上部和左下的兩個點,假設另一個點為P3,而k0是P1,P2間的斜率,k1是指P1,P3間的斜率,k2是指P2,P3間的斜率. (各字母的意義見圖1)

      您所在的用戶組無法下載或查看附件:triangle.jpg。
      這樣,三角形的三條邊就可以方便的表達出來了:
      如直線P1P2的二維直線方程為 y=k0(x-x1) y1.
      P1P3: y=k1(x-x1) y1
      P2P3: y=k2(x-x2) y2這樣,判定一個點是否在三解形內,就只要判定這個點是否在三條邊指向三角形內的一側.這里,假如要判的點為p(x’,y’),則根據(jù)圖1的情況,有:
      If(k0(x’-x1) y1>=0&&k1(x’-x1) y1<=0&& y’>=y2)//考慮到P2P3是水平的情況
      {
      Collide!
      }
      Else
      {
      Safe Condition.
      }顯然,這個算法并不算得上好,因為假如三解形旋轉的話,原來的某直線的左側意味著三角形的內側可能就會意味著外側。這時,可以考慮再增加一個三元組,用來實時指示當前的三條直線指向三角形內側的方面,可取的情況有以下幾種:
      a)左側 b)右側 c)上側(水平時) d下側(水平時)

      4實現(xiàn)部分的要害代碼(AS2)
      4.1粒子類:
      import SMotion.*
      import SColDet.*

      class SPhy.CSPhyMc extends MovieClip
      {
      public var m:Number=0;//mass

      public var g:Number=0;//gravity

      public var pF:Number=0;//Positive forces,attention here UpCase!!!!!!!
      //Because the compiler was not so perfect as you think ,add a p here to prepare for the case.
      public var r:Number =0;//when it become a ball---radius.

      public var v :Number=0;//1 demision Velocity or together Velocity of vx ,vy
      public var vx:Number=0;
      public var vy:Number=0;

      public var f :Number=0;//fraction forces.
      public var fx:Number=0;
      public var fy:Number=0;

      public var a :Number=0;//acclerate v
      public var ax:Number=0;
      public var ay:Number=0;

      //plane game use;
      public var bigFire:Number=0;

      private static var DEAD:Number=0;
      private static var LIVED:Number=1;
      private var life:Number;

      private var mMotionCom:RCSMove;
      private var mColDetCom:RCSColDet;

      private static var thisP:Object;

      public function setLife(lifeValue:Number):Void
      {
      life=lifeValue;
      }

      public function getLife():Number
      {
      return life;
      }

      public function isLived():Boolean
      {
      return life==LIVED;
      }

      public function init():Void
      {
      thisP=this;
      this.vx=0;
      this.vy=0;
      this.v=3 random(3);

      this._width=10;
      this._height=10;
      this.r=5;

      this.initCom();
      }



      public function initPos(targetPlane:CSPhyMc):Void
      {
      var randNum:Number=random(100);
      //set init positoin:down,left,up,right
      if(randNum<25)
      {
      this._x=random(Stage.width);
      trace("Width" Stage.width "Height" Stage.height);

      this._y=Stage.height;
      }
      else if(randNum<50)
      {
      thisP._x=_root.gStageLeft;
      this._y=random(Stage.height);
      }
      else if(randNum<75)
      {
      this._x=random(Stage.width);
      thisP._y=_root.gStageTop;
      }
      else
      {
      this._x=Stage.width;
      this._y=random(Stage.height);
      }

      this.CalVx_Vy(this,targetPlane);
      }

      private function GetDis(mc1:CSPhyMc, mc2:CSPhyMc):Number
      {
      return Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x) (mc1._y-mc2._y)*(mc1._y-mc2._y));
      }

      private function CalVx_Vy(mcChase:CSPhyMc, mcAim:CSPhyMc):Void
      {
      var len:Number= GetDis(mcChase, mcAim);
      mcChase.vx=(mcAim._x-mcChase._x)/len*mcChase.v;
      mcChase.vy=(mcAim._y-mcChase._y)/len*mcChase.v;
      }

      public function initCom():Void
      {
      mMotionCom=new RCSMove();
      mColDetCom=new RCSColDet();
      }

      public function outDetect():Boolean
      {
      var offset:Number=25;
      return mColDetCom.particleOutDet(this,0-offset,0-offset,Stage.width 2*offset,Stage.height 2*offset);
      }

      public function move_show():Void
      {
      mMotionCom.Move2D(this,this.vx,this.vy);
      }

      public function collideDect(targetPlane:CSPhyMc):Boolean
      {
      if(_root.mcLibPlaneName=="ball")
      return mColDetCom.TwoBall(targetPlane,this);
      //return this.hitTest(targetPlane.getBounds(_root).xMin,targetPlane.getBounds(_root).yMax,false);
      }
      }4.2游戲主調度類
      class ChaseAim
      {
      static private var thisP:Object;
      private var staturs:Number;//gaming 1,failure 0
      private var speed:Number;
      private var bulletNum:Number=20;
      private var start:Number=0;
      private var end:Number=0;
      public function init():Void
      { thisP=this;
      staturs=1;
      speed=3;
      bulletNum=20;
      for(var i=0;i<11;i )
      {
      _root.createTextField("txt" i,i,0,(i-1)*25,500,25);
      }

      _root.attachMovie("ball","ball1",11);
      _root.ball1._x=250;
      _root.ball1._y=200;
      _root.ball1.r=20;

      for(var i=0;i<bulletNum;i )
      {
      _root.attachMovie("bullet","bullet" i,20 i);
      _root["bullet" i].vx=0;
      _root["bullet" i].vy=0;
      _root["bullet" i].v=3 random(3);
      _root["bullet" i].r=5;
      _root["bullet" i]._width=10;
      _root["bullet" i]._height=10;

      GenBullet(_root["bullet" i]);
      }
      start=getTimer();

      setInterval(EffectF,100);
      }

      private function EffectF():Void
      {
      if(thisP.staturs!=0)
      {
      for(var i=0;i<thisP.bulletNum;i )
      {
      if (thisP.CheckOutBounds(_root["bullet" i])) thisP.GenBullet(_root["bullet" i]);
      if(thisP.TwoBallCol(_root.ball1,_root["bullet" i]))thisP.staturs=0;
      thisP.Move2D(_root["bullet" i]);
      //_root.txt3.text=_root["bullet" i].vx;
      //_root.txt4.text=_root["bullet" i].vy;

      }


      if( Key.isDown(Key.LEFT))_root.ball1._x -= thisP.speed;
      if( Key.isDown(Key.RIGHT))_root.ball1._x = thisP.speed;
      if( Key.isDown(Key.UP))_root.ball1._y -= thisP.speed;
      if( Key.isDown(Key.DOWN))_root.ball1._y = thisP.speed;
      if(thisP.staturs==0)
      {
      _root.txt0.text="you failure";
      thisP.end=getTimer();
      var tmp:Number=thisP.end-thisP.start;
      _root.txt1.text="你共堅持了" tmp/1000 "秒";
      //delete this.onEnterFrame;
      }
      }
      }

      private function GenBullet(tmpMc:CSPhyMc):Void
      { var left:Number;
      var top:Number;
      if(random(2))
      {
      left=random(7)*100-100;
      top=random(2)*400;
      }
      else
      {
      left=random(2)*600;
      top=random(6)*100-100;
      }
      tmpMc._x=left;
      tmpMc._y=top;
      CalVx_Vy(tmpMc, _root.ball1);
      }

      private function CheckOutBounds(tmpMc:CSPhyMc):Boolean
      {
      if(tmpMc._x<-10||tmpMc._x>510||tmpMc._y<-10||tmpMc._y>410)
      return true;
      else return false;
      }

      private function TwoBallCol(ball1:CSPhyMc,ball2:CSPhyMc):Boolean
      {
      if(Math.sqrt((ball1._x-ball2._x)*(ball1._x-ball2._x) (ball1._y-ball2._y)*(ball1._y-ball2._y))<=(ball1.r ball2.r))
      return true;
      else
      return false;
      }

      private function GetDis(mc1:CSPhyMc, mc2:CSPhyMc):Number
      {
      return Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x) (mc1._y-mc2._y)*(mc1._y-mc2._y));
      }

      private function CalVx_Vy(mcChase:CSPhyMc, mcAim:CSPhyMc):Void
      {
      var len:Number= GetDis(mcChase, mcAim);
      mcChase.vx=(mcAim._x-mcChase._x)/len*mcChase.v;
      mcChase.vy=(mcAim._y-mcChase._y)/len*mcChase.v;
      }

      private function Move2D(mc:CSPhyMc):Void
      {
      mc._x =mc.vx;
      mc._y =mc.vy;
      }
      }

      4.3核心運行函數(shù):
      function mainLoop():Void
      {
      UserPlaneControl();
      if(_root.gRunFlag)
      {
      //trace("yes");
      for(var i:Number=0;i<_root.gBulletNum;i )
      {
      //trace("yes");
      if(!_root[mcUserBulletName i].isLived())
      {
      //trace("relife:" i);
      _root[mcUserBulletName i].setLife(LIVED);
      _root[mcUserBulletName i].init();
      _root[mcUserBulletName i].initPos(_root[mcUserPlaneName]);
      }
      else if(_root[mcUserBulletName i].outDetect())
      {
      //trace("outDetect:" i);
      _root[mcUserBulletName i].setLife(DEAD);
      }
      else
      {
      _root[mcUserBulletName i].move_show();

      if(_root[mcUserBulletName i].collideDect(_root[mcUserPlaneName]))
      _root.gRunFlag=false;
      }
      }
      }
      else
      {
      var timeCount:Number=0;
      //clear the main game scence
      clearInterval(_root.gIntervalID);
      /*for(var i:Number=0;i<_root.gBulletNum;i )
      _root[mcUserBulletName i].removeMovieClip();
      _root[mcUserPlaneName].removeMovieClip();
      */
      _root.gTimeEnd=getTimer();
      timeCount=_root.gTimeEnd-_root.gTimeStart;
      trace("you last:" String(timeCount) "secs.");
      trace2("You lasted:" String(timeCount)/1000 "secs.");
      /*start the end mc
      _root.gNextScence=0;
      _root.gIntervalID=setInterval(showEnd,_root.fps,_root.mcEndName,_root.result);
      */
      }
      }5實驗結論:
      通過該模型,實現(xiàn)了一個粒子系統(tǒng)的基本運作模式,該運作模式同樣適用于其它的粒子系統(tǒng),只要在最要害的運動及顯示部分加以變換即可.

      分享:在FLASH中輕松畫花教程

      來源:閃吧//所屬分類:Flash教程/更新時間:2008-03-05
      相關Flash教程