allwiki首页  
天下维客 你可以修改的网络知识库
首页最近更改优秀条目专题展示电脑科技词典软件学习网络知识电脑安全明星时尚天下百科
 

Flash制作飞机躲避小游戏

天下维客,你可以修改的网络知识库

Jump to: navigation, search
Flash教程
内容
实例教程:
其它教程
外部资源

是男人就撑100秒是一个流行广泛,但又略显BT意味的小游戏。游戏的玩法就是四周不断的有子弹射出,而你的任务就是控制你的飞机不断的躲避,直到被击中,以躲避时间的长短来评定游戏水平的高低。

目录

综述

这个游戏在实现是比较容易的,由于子弹在这里占据了主要地位,所以考虑以子弹为中心,即考虑构建一个粒子系统,来控制子弹的发射,发射方向的计算,以及出界的判定等。至于飞机方面,则只要有控制的部分(事件驱动,事件监听或用循环,要视具体实现环境而定),把两者结合起来,只要加上飞机与子弹间的碰撞检测即可,这里出于演示的目的,简单起见,采用球代替飞机的造型。

子弹粒子系统的运作流程

子弹的粒子系统要控制好子弹的发射,发射方向的计算,出界的判定以及碰撞检测. 该粒子系统的总体框架并不困难,这里给出我实现过程中的总体框架: 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 中的条件判定对应的现实意义,即是否会出现实中无意义但在程序中却出现的情况,如果出现的话,这样的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)此时,将导致第二个判定永远无法到达.
    • 所以,当条件复杂且多的时候,最好是列张真值表,看看所有可能的情况是否都如期的到达该到的判定条件处,避免在程序调试中浪费过多不避要的时间.

子弹粒子设计细节

子弹粒子的数据结构及存储方式

  • 粒子类以一个类的形式进行封装,里面包含了一些基本的物理属性及粒子相关的一些动作(函数)。简要的情况如下:

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;
      }
      … …

}

  • 而在游戏中,这采用一个数组来实现粒子的群落,理由是使用方便而且快速。(当然,出于一种美学上的要求,你可能会选链表,因为它的插入和删除来的比较漂亮和干净,这就取决于你自己的喜好了)
  • 至于子弹起始的坐标值,则是随机的散落于游戏屏幕区域外围,要写个相应的算法并不困难(详见代码部分)

发射角的计算

发射角的计算相当于一道简单的高中向量的题目: 已知两点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)

碰撞检测

  • 碰撞检测是个广泛而重要的话题,可以从简单到复杂,难度突破主要在计算几何上。这里针对本游戏谈两个:
  1. 两个圆的碰撞检测,这个不用多说了,只要看两个圆的圆心的距离是否比它们的半径之和来的小就是了.

即圆1有:圆心O1(x1,y1),半径r1

 圆2有:圆心O2(x2,y2),半径r2
 则它们之间的碰撞检测可以这样来做:
     
If(Len(O1O2)<=r1+r2)
      {
             Two circles collide.

} Else {

      Safe condition.

} 如果在视觉效果要求比较高的场和,尤其是不允许出现物体重叠的场和,不仿在Len(O1O2) 后加上一个偏移值。这样可以保证视觉上不会看到两个物体重叠的现像,尽管在精确的数值模型上二者并未相碰。而在数值精度要求高的场和,恐怕情况就要反一下了,图形是第二位的,数据的精准才是最重要的。具体如何去平衡图形和数据间的对应关系,还请诸位自己去斟酌了。

  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间的斜率.
  • 这样,三角形的三条边就可以方便的表达出来了:

如直线P1P2的二维直线方程为 y=k0(x-x1)+y1.

        P1P3:  y=k1(x-x1)+y1
        P2P3:  y=k2(x-x2)+y2
  • 这样,判定一个点是否在三解形内,就只要判断这个点是否在三条边指向三角形内的一侧.这里,如果要判的点为p(x’,y’),则根据图1的情况,有:

If(k0(x’-x1)+y1>=0&&k1(x’-x1)+y1<=0&& y’>=y2)//考虑到P2P3是水平的情况 {

             Collide!

} Else {

             Safe Condition.

}

  • 显然,这个算法并不算得上好,因为如果三解形旋转的话,原来的某直线的左侧意味着三角形的内侧可能就会意味着外侧。这时,可以考虑再增加一个三元组,用来实时指示当前的三条直线指向三角形内侧的方面,可取的情况有以下几种:

a)左侧 b)右侧 c)上侧(水平时) d下侧(水平时)

实现部分的关键代码

粒子类

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);
      }

}

游戏主调度类

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
Personal tools
工具
金银币拍卖 金币拍卖预展  金银币网店 熊猫金银币 生肖金银币