您的位置:首页 > 理论基础 > 数据结构算法

关于平面战机射击游戏的一点小结

2011-05-11 22:00 357 查看
很久以前刚学TC开发图像类程序的时候就兴起了自己写个小射击游戏的想法,前前后后花了将近一年的功夫在当时的8086上实现了.

后来进入windows时代,学习了VB就又想起了这个东西,又用VB实现了一次. 抛开语言上的细节,实质上的算法基本是一致的. 

闲来无事,就在此小结一番.谈谈心得体会,希望对大家有所助益吧.

 

<此处只谈算法,因此即使涉及代码也是伪代码,请勿直接使用>

 

由繁入简, 将游戏细节简略到最少的程序. 可以得到如下几个方面:

1: 游戏对象分类

    从2D战机射击来说, 可以粗略分为: 我方战机, 敌方战机, 子弹

    除开贴图的区别, 从程序角度来说, 它们的区别在于:

    a:我方战机飞行轨迹随时可控

    b:敌方战机飞行轨迹不可控

    c:子弹飞行轨迹半可控(即发射时的位置是可控的)

 

2: 按键控制

    既然是简化的, 只考虑方向和射击, 上下左右和射击,一共5个按键

 

3: 飞行位置和碰撞检测

    我方战机,敌方战机,子弹的飞行速度各有差异(如果想要做得略微复杂点,可以考虑引入加速度)

    碰撞检测, 最简单的就是距离检测:

    a: 先要设定三类对象的尺寸(即敌我战机和子弹大小), 不能简化为一个无大小的点(因为这样几乎永远谁也碰不到对方)

    b: 我方战机和所有的敌方战机需要逐一判断距离

    c: 所有敌方战机和所有子弹需要逐一判断距离

    d: 需要事先将敌我战机和子弹的中心位置设定为贴图的中点(因为贴图是以图片左上角为0,0点的, 但是这样会给碰撞检测带来很大的麻烦)

    e: 需要非常精简并且几乎无误的判断(碰撞检测允许略微粗糙的计算, 因为实际上敌我双方如果只差1,2个像素也算它们碰到也无妨,但是因为碰撞检测是在游戏每一帧中都要来一遍的,因此精简的算法将带来程序效率极大的提高)

 

4: 画面

    按照敌我战机和子弹的当前位置分别将图片一一贴至指定位置, GDI也可以,DX也可以, OPENGL也可以, 有多少本事就干多少

 

5: 其他

    对象和场景的初始化

 

下面分析一下程序大体流程

抛开开场动画之类的东东, 整个程序是一个无限循环(直到我方战机死光光,因为敌机一般是死不完的,子弹一般也是打不光的)

因此主循环可以放在Timer里面也可以放在循环里面, 我C版用的是for(;;){}  VB版用的是Timer

主循环里面放点什么呢?

 

For (或者Timer)

    贴图

         '循环刚开始第一次贴图的位置由程序初始化来定

 

    按键控制

         '检测哪个键被按下,哪个键被松开 VB里面有对应的KeyDown和KeyUp事件,很简单

 

    飞行控制和碰撞检测

         '按照上面的按键去改变我方战机的位置, 如果发射键按下则初始化子弹位置为我方战机当前位置, 并设置子弹被激活

         '依次判断敌我战机, 地方战机和子弹的间距, 如果小于双方半径就算碰到了(有功夫的就在贴图环节里面贴一个爆炸的图)

Next

就这么点东西, 简单不?

 

下面稍微说一点详细的.

从游戏对象来看,敌我战机,子弹其实除了贴图,控制,速度,没有本质上的区别. 因此可以沿用相同的数据结构

大体如下:

Type FlyOBJ    (对应C的struct)

        '当前中心位置

Xo As Long  

        Yo As Long

'之前中心位置

Xold as long

Yold as long

        '生命

        Life as Long

         '尺寸(半高和半宽尺寸)

         Hei As Long

         Wid As Long

 

        '当前速度

Vx as Long

        Vy As Long

 

'最大速度

VxMax as long

Vymax as long

 

        '加速度

        Ax as Long

Ay As long

 

'贴图 1 (静态)

Img As Long  '指向一个图片或者一个图片的某个部分等等,只要你自己能知道就行

Bkg as Long  '指向一个用于保存贴图背景的图片对象或内存之类的,后面会解释

 

'贴图 2 (动态)'如果想要战机和子弹有动画效果则可以用数组来指定多个贴图,并增加一个变量用以指定当前所要贴的是哪一个

  Img(n) as long  '用于存放动态效果的一系列图片

        ImgIdx as long  '目前显示哪一个

Bkg as Long

 

'状态, 用于保存战机存活状态或子弹发射状态

Activ as boolean

End Type

 

最后定义:

DIM MYPLANE AS FlyOBJ

DIM ENM(100) AS FlyOBJ

DIM BLT(200) AS FlyOBJ

上面三行就定义我方战机1个, 地方战机101个,子弹201发,  不明白为何是101和201的回去翻VB书. C里面就是100和200个

 

按键控制部分,定义5个变量用来记录按键状态

Dim Kup as long

dim KDwn as long

dim KLft as long

dim KRgt as long

dim KFire as long

然后在窗体的Keydown和keyup事件中分别记录按键

form_keydown()过程

   Select case keycode  ' 就是Switch

      case 上: Kup =1, KDwn =0

      case 下: KDwn =1, Kup =0

      case 左: KLft=1, Krgt =0

      case 右: Krgt=1, klft =0

      case 发射: Kfire =1

...

过程结束

说明: 因为上和下, 左和右是对立的, 因此按了"上"就默认"下"已经抬起,按了"左"就默认"右"已经抬起, 反之亦然 , 只有发射键没有逻辑上冲突的按键,因此直接设置为1就行

 

按键抬起的事件更简单了:

Form_KeyUp()过程

   Select case keycode   ' 就是Switch

 

      case 上: Kup =0

      case 下: KDwn =0

      case 左: KLft =0

      case 右: Krgt =0

      case 发射: Kfire =0

...

过程结束

 

 

 

飞行位置和碰撞检测分开讲一下:(以水平方向为例)

先保存当前位置: Xold=Xo, Yol=Yo

飞行位置:

位置 Xo = Xold + Vx

速度 Vo = Vx + Ax (并判断其绝对值是否大于VxMax)

Y方向计算一样,不赘述.

1: 对于敌方战机, 可以简单的设置一个初始速度Vx和Vy即可, (进阶要增加难度的话可以增加加速度的计算)

     2:我方战机需要完整套用上述公式来计算加速度,速度和位置(根据按键控制得到的控制方向决定加速度的正负)

3:子弹可以简单的设置一个初始速度Vx和Vy即可,(进阶要增加难度的话可以增加加速度的计算)

碰撞检测:

1:我方战机和敌方战机

        for i=0 to 敌方战机数量

if  ABS( Myplane.Xo - ENM(i).Xo |)水平中点距离是否小于两者宽度之和 MYPlane.Wid + ENM(i).wid ? then

if 再判断一下两者垂直距离距离是否小于两者高度之和? then

如果也是, 那么碰到啦,我方战机和敌机都处理各自的Life值,等于零的话就初始化位置\速度和生命.

end if

                        end if

next

 

2: 子弹是否击中敌机, 比上面那个再多一重循环, 因为子弹和敌机都是数组,所以多一个循环, 不写伪代码也能明白的吧?

 

3: 出界问题:

    
a:如果子弹飞出屏幕就算出界了, 将状态重新设为未激活.

    
b:敌机的话, 假定是从上向下飞, 那么只要其Y大于屏幕高度就算出界, 然后重新初始化其位置和速度(横版的话就得是水平位置和屏幕宽度比了

c:我方战机一般不玩出界的(以前有见到过左面出界右面出来的那种), 如果X或Y超出屏幕界限则将Vx或Vy设为0

 

4: 增加复杂度的运算(比如子弹和敌机加速度和简单智能)

举例: 敌机根据自身位置在我方战机的左侧或右侧调整加速度(正负), 只调整X方向加速度, Y方向无加速度(否则追到你死,但是你可以控制我方飞机让它永远追不到),

子弹根据距离最近的敌机位置调整加速度等等(这个计算量很大,不是很推荐,几百个子弹和敌机的计算量不小,)

 

画面贴图部分:

子弹贴子弹的图片,战机贴战机的图片,该贴几号图就贴几号图(有动画效果的话),以我方战机为例:

Draw()过程

With MYPLANE

贴 .Img(.ImgIdx)号图片 到 (.Xo-.Wid), (.Yo-.Hei)处

.ImgIdx = (.ImgIdx+1) mod 贴图动画数量   '(就是定义Img(n)中的n) ,本句是将贴图索引指向下一个图片

  end with

...

过程结束

贴图基本就是这样, 但是实际上还要复杂一些, 因为还要有背景方面的一些处理. 如果不处理背景的话,你会发现飞机飞过会留下一道华丽的残影.

加上背景处理的贴图流程大体如下:

1: 将图片从Bkg处贴至上一个中心位置(Xold,Yold)

2: 从即将要贴图位置(Xo,Yo)的当前背景按照贴图对象的大小(宽,高)扣至内存或图片对象Bkg

3: 将目标贴图贴至(Xo,Yo)处

 

至于第一个Bkg和Xold,Yold从哪里来,就由战机和子弹的初始化决定

 

初始化:

 

a:最大速度  子弹>敌方战机>我方战机

b:加速度  我方战机>敌方战机>子弹

 

我方战机: 假定是屏幕底部的中间,Xo=屏幕一半, Yo=屏幕高度, Vx=Vy=0, (别忘记将Xo,Yo处上下Hei左右Wid大小的一块背景先拷贝到Bkg.

地方战机: 假定是屏幕顶部,Yo=0, Xo=Rnd*屏幕宽度,随机吧,  初始速度Vx和Vy都可以取绝对值不超过VxMax和VyMax的值(如果是从上至下的话Vy请设为正值,Vx随意, 因为有出界判断)

子弹: Activ总是为false, 直到按下发射键,才设置Activ为True

 

 

我还忘记写什么吗?

背景音乐和音效什么的被我精简掉了,哈哈, 翻翻API什么的容易解决, 反正这里只是说个大概的东西, 我本人也只是自己琢磨的. 

以前写的那个打飞机小游戏源码不知道扔哪里去了, 下次找出来公布一下.

对了, 上面说的那个半成品游戏在这里:  >>>传送门<<< 只有一个exe, 只要电脑上有VB运行库就可以运行了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息