辣鸡

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

注:本系列教程每周一篇,旨在引导刚刚接触FLASH的新手通过实例进行游戏开发的学习。在过程中逐步说明涉及到的类及对应的使用方法。从一个光秃秃的方块开始,根据不同的控制方式、玩法产生不同的分支,最终完善成一个个可玩的游戏。希望对各位入门的朋友有所帮助!在教程涉及的各种处理方法,可能不够完善,也希望各位高手指正:)

转载请注名来源于天地会

第五篇 告别方块,添加碰撞

上一篇教程中。我们新增了基地的概念,并让敌人可以向基地开火。但是,敌人和自己还都是四四方方一大块,分不清哪是脸哪是屁股。另外在游戏界面里,各个对象依然可以重叠在一起。这显然不太合理。在本次的教程中,我们将一一修正这些问题。

首先,换个脸。在FLASH里对原有的组件进行编辑,我们随便画一个坦克的样子,大概像下面这样:

1.jpg 2.jpg 

这样,就有了比较像样(至少不是方块)的素材了。不过,新的问题出现了。这个时候测试影片,我们可以发现,当坦克向上开的时候,是没问题的,但是向其他方向移动,素材并没有发生变化,而是横着走,或者倒退了。我们需要根据坦克的方向来调整素材的旋转角度。于是,我们重写了FaceObject的direction方法:

  1. override public function set direction(dir:uint):void
  2.                 {
  3.                         super.direction = dir;
  4.                         
  5.                         var angle:Number;
  6.                         var tx:Number;
  7.                         var ty:Number;
  8.                         
  9.                         switch(dir)
  10.                         {
  11.                                 case ActionObject.UP:
  12.                                         angle = 0;
  13.                                         tx = 0;
  14.                                         ty = 0;
  15.                                         break;
  16.                                 case ActionObject.RIGHT:
  17.                                         angle = Math.PI * 0.5;
  18.                                         tx = _face.width;
  19.                                         ty = 0;
  20.                                         break;
  21.                                 case ActionObject.DOWN:
  22.                                         angle = Math.PI;
  23.                                         tx = _face.width;
  24.                                         ty = _face.height;
  25.                                         break;
  26.                                 case ActionObject.LEFT:
  27.                                         angle = Math.PI * 1.5;
  28.                                         tx = 0;
  29.                                         ty = _face.height;
  30.                                         break;
  31.                         }
  32.                         
  33.                         _face.transform.matrix = new Matrix(Math.cos(angle), Math.sin(angle),Math.sin(angle)*-1,Math.cos(angle),tx,ty);
  34.                 }
复制代码

从代码中我们可以看出,是通过设置的方向值的不同,获取了一个特定的角度(向上0度,向右90度,向下180度,向左270度。注意这里的angle是用弧度来表示角的。)最后,我们定义了一个矩阵,并通过_face的transform来进行了形状的变化。

有兄弟可能会问为什么需要tx,ty两个属性呢?你可以把最后一行里的tx和ty去掉测试一下。可以发现,当素材旋转的时候,产生了位移。这是由于我们使用的素材的注册点是在左上角(可以看到图片上左上角那个十字么)。而旋转是围绕注册点进行的,也就是说,坦克的旋转是按照以下方式进行的:
3.jpg 

可以看到,在旋转的过程中,坦克由于环绕左上的注册点旋转,实际上在4个方位各产生了与本身的宽度和高度相关的位移,因此,我们需要把他们移动相应的位置,使他们看起来还在原来的位置上。因此使用了tx,ty。

矩阵(Matrix)是一个非常有用的类。在我们的教程中也是第一次用到。矩阵的构造函数:

new Matrix(a,b,c,d,tx,ty)

其中各参数的说明如下:
4.jpg

Adobe官方提供的文档中有详细的说明。这里不再过多描述,你可以看这里查看详细的说明:http://help.adobe.com/zh_CN/AS3L ... sh/geom/Matrix.html

现在,坦克的方向可以很清楚的看到了,下面,我们来看一下如何放置游戏场景里的物品与其他的物品撞到一起。我们来进行碰撞检测。

首先,随着游戏的扩展,并不是所有的游戏物体都能碰到(比如草丛可以踩过去等等),因此,游戏对象需要增加一个开关,来控制他是否参与碰撞检测.我们修改gameObject类,增加这个属性:

  1. /**
  2.                  * 是否参与碰撞检测 
  3.                  */
  4.                 protected var _hitTest:Boolean = false;
  5.                 /**
  6.                  * 是否参与碰撞检测 
  7.                  */
  8.                 public function get hitTest():Boolean
  9.                 {
  10.                         return _hitTest;
  11.                 }
复制代码

为什么设置为protected?因为我们不需要从外部来更改它的碰撞属性,而只需要读取是否可以碰撞就可以了。

gameObject是继承自Sprite对象的。而Sprite提供了两种检测碰撞的方法,一是hitTestObject,另一个则是hitTestPoint。从APIhttp://help.adobe.com/zh_CN/AS3L ... /DisplayObject.html)中我们可以很清楚的看到,hitTestObject只是进行了外框的检测,也就是说,如果我们的图形不是正方形,而是其他不规则图形,哪怕图形上没有发生碰撞,只要图形的范围发生了交叠,FLASH就认为他们碰撞了。这显然不符合我们的要求(虽然我们的坦克基本上是个方块)。因此,我们还是和判断子弹的碰撞一样,选择hitTestPoint。但hitTestPoint需要一个指定的点。如何处理呢?

我们定义了8个检测目标,分别位于图形的四周(如下图所示),如果这8个目标点发生了碰撞,就认为物体撞在了一起。
1.png 
由于只有ActionObject才需要检测碰撞,所以,我们根据上图给ActionObject增加检测点数组:

  1. /**
  2.                  * 碰撞检测点
  3.                  */
  4.                 protected var _hitPoint:Array = [[0,0],[22,0],[45,0],[45,22],[45,45],[22,37],[0,45],[0,22]];
复制代码

进而修改nextCanMove方法:

  1. /**
  2.                  * 下一目标点是否可以移动
  3.                  */
  4.                 public function get nextCanMove():Boolean
  5.                 {
  6.                 ...
  7.                 // 如果下一目标点超出屏幕范围,则不能移动
  8.                         if (nx > Global.stage.stageWidth - width || nx < 0) return false;
  9.                         if (ny > Global.stage.stageHeight - height || ny < 0) return false;
  10.                         
  11.                         // 计算下一目标点的碰撞测试(本部分为新增代码)
  12.                         for each(var obj:gameObject in Global.scene.AllObject)
  13.                         {
  14.                                 if (obj == this || !obj.hitTest) continue;
  15.                                 for each(var p:Array in _hitPoint)
  16.                                 {
  17.                                         if (obj.hitTestPoint(nx + p[0], ny + p[1], true)) return false;
  18.                                 }
  19.                         }
  20.                 ...
  21.                 }
复制代码

我们循环了游戏场景中的所有游戏对象,如果发现游戏对象是自己或者对象并不参与碰撞检测,跳过检测,接下来,循环数组中的8个位置点,逐个判断是否发生了碰撞。如果发生,则返回false。

ActionObject是肯定参与检测的,因此,我们修改了一下ActionObject的构造函数:

  1. public function ActionObject(ctrl:basicController) 
  2.                 {
  3.                         super();
  4.                         controller = ctrl;
  5.                         controller.target = this;        // 将控制器的控制目标设置为自己
  6.                         _hitTest = true;// 打开碰撞检测开关
  7.                 }
复制代码

在加入碰撞检测后,我们原来的子弹飞行也可以用碰撞检测开关来判断了,因此,我们修改了BulletObject的Do方法:

  1. for each(var obj:gameObject in Global.scene.AllObject)
  2.                         {
  3.                                 if (obj.hitTest && obj.part!=_shooter.part && obj!=this && obj.hitTestPoint(x, y, true))
  4.                                 {
  5.                                         // 击中目标
  6.                                         if(obj.hasOwnProperty('Hurt')) (obj as FaceObject).Hurt(20);
  7.                                         die();
  8.                                         break;
  9.                                 }
  10.                         }
复制代码

我们改成了用碰撞开关来控制是否进行判断的前提条件,而把是否伤血由属性Hurt来判定。这样,子弹在击中障碍的时候,也会消失,但障碍物不会伤血(因为障碍物可能不具备Hurt方法)

到此,碰撞检测和基本的美化就已经做完了。不过,我的程序的运行界面是这样的:
5.jpg

实际运行swf
 main.swf (20.05 KB) 

可能你的和我的不太一样吧。呵呵。可以从下面的源码中找到不同。相信经过前面4篇的学习,可以很轻松的看懂现在的源码的:

 Teach.rar (408.24 KB) 

在下一篇教程中,我们将研究如何更简单的实现地图,同时做一些必要的限制(比如现在可以像冲锋枪一样不停的发射子弹,如果对于一款坦克游戏来讲,这显然不太符合逻辑)。

最后,在这里提醒各位在看本教程的朋友。教程只为引导大家快速写出可以运行的东西,而非进行项目实战。因此一些处理方法并不是最好的,而是比较容易理解和操作的。例如本篇教程中的碰撞检测。对于场景中对象不多的游戏可以胜任,如果游戏对象多起来,效率就会大大降低。选择合适的算法、处理方式,是一个漫长的路程,需要经验的积累和沉淀:)

posted on 2011-12-28 16:14  辣鸡  阅读(505)  评论(0编辑  收藏  举报