zoukankan      html  css  js  c++  java
  • 从零开始学AS3游戏开发【五】 告别方块,添加碰撞

    注:本系列教程每周一篇,旨在引导刚刚接触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) 

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

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

  • 相关阅读:
    Markdown语法帮助文档
    react-native-vector-icons使用方法
    如何创建Pull Request,以开源项目ant design pro为例
    4.环境变量总结篇
    3.Flutter之hello_world
    构建之法 阅读笔记03
    学习进度14
    团队项目-个人博客6.5
    团队项目-个人博客6.4
    构建之法 阅读笔记02
  • 原文地址:https://www.cnblogs.com/keng333/p/2304962.html
Copyright © 2011-2022 走看看